Hello friends! How are you today? Today we're going to discuss a project that is interesting and also useful in our everyday life. You see QR codes almost everywhere, right? They are printed on almost every product's package, leaflets, newspapers, and brochures.
Perhaps, you often use QR code scanners on your mobile device. What about making such a program by yourself? Yes! That is exactly what we are going to do today. We will make a QR code scanner using the ESP32-CAM. For image processing, we will use the OpenCV library.
If you’ve ever wanted to create a real-time QR code scanner using a low-cost, wireless camera module, you’re in the right place. In this tutorial, we’ll walk through setting up an ESP32-CAM to stream video and using OpenCV to detect and decode QR codes in real time.
The ESP32-CAM is a powerful yet affordable development board that combines the ESP32 microcontroller with an integrated camera module, making it an excellent choice for IoT and vision-based applications. Whether you're building a wireless security camera, a QR code scanner, or an AI-powered image recognition system, the ESP32-CAM provides a compact and cost-effective solution.
One of its standout features is built-in WiFi and Bluetooth connectivity, allowing it to stream video or capture images remotely. Despite its small size, it packs a punch with a dual-core processor, support for microSD card storage, and compatibility with various camera sensors (such as the OV2640). However, since it lacks built-in USB-to-serial functionality, flashing firmware requires an external FTDI adapter.
This project consists of two main components:
ESP32-CAM as an Image Server
Python Script for QR Code Detection and Processing
Each component interacts with different subsystems to achieve the overall functionality.
The architecture consists of:
ESP32-CAM: Captures images and hosts them on a web server.
WiFi Network: Enables communication between ESP32-CAM and the computer running the Python script.
Python Script on a Computer: Continuously fetches images from ESP32-CAM, processes them, and extracts QR code data.
User Interface: Displays the live feed and detected QR codes.
Hardware: ESP32-CAM module with OV2640 camera.
Software: ESP32-CAM uses the esp32cam library to initialize the camera and serve images via an HTTP web server.
Functionality:
Captures an image when accessed via http://
Returns the image in JPEG format to the requesting client.
ESP32-CAM initializes camera settings (resolution: 800x600, JPEG quality: 80).
It connects to a WiFi network.
A web server starts on port 80.
When a client (Python script) accesses /cam-hi.jpg, ESP32-CAM captures an image and sends it.
Hardware: A computer (Windows/Linux/Mac).
Software: Python, OpenCV, NumPy, urllib.
Functionality:
Fetches images from ESP32-CAM at regular intervals.
Converts them to grayscale for better QR detection.
If normal detection fails, applies adaptive thresholding.
Detects and decodes QR codes using OpenCV.
Displays the live video feed with detected QR code data.
The script continuously requests images from http://
It decodes the image using OpenCV.
Converts the image to grayscale.
Attempts to detect a QR code.
If detection fails, applies image preprocessing (blurring and thresholding).
If a QR code is found, it prints the decoded text and overlays a bounding box.
The processed frame is displayed in a window.
ESP32-CAM Captures Image
Uses esp32cam::capture() to take a snapshot.
Hosts the image on an HTTP endpoint (/cam-hi.jpg).
Python Script Requests Image
Sends an HTTP GET request using urllib.request.urlopen().
Receives the image data in JPEG format.
Image Processing & QR Code Detection
OpenCV converts the image to grayscale.
Tries decoding the QR code using cv2.QRCodeDetector().detectAndDecode().
If unsuccessful, applies adaptive thresholding and retries.
Output Display & User Interaction
If a QR code is detected, its content is displayed.
Bounding boxes are drawn around detected QR codes.
Live video feed is displayed in an OpenCV window.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module |
1 |
FTDI USB to Serial Converter 3V3-5V |
1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
Following is the circuit diagram of this project.
Fig: Circuit diagram
ESP32-CAM WiFi + Bluetooth Camera Module |
FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
If this is your first project with an ESP32 board, you need to do board installation. You will also need to download and install the ESP32-CAM library. To make the camera functional, the cp210x usb driver and the FTDI driver, must be properly installed in your computer. Here is a detailed tutorial that shows how to get started with the ESP32-CAM.
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WebServer server(80);
static auto hiRes = esp32cam::Resolution::find(800, 600);
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
void loop()
{
server.handleClient();
}
After uploading the code, disconnect the IO0 pin of the camera from GND. Then press the RST pin. The following messages will appear.
Fig: Code successfully uploaded to ESP32-CAM
You have to copy the IP address and paste it into the following part of your Python code.
Fig: Copy-pasting the URL to the Python script
#include
#include
#include
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WIFI_SSID and WIFI_PASS: Define the SSID and password of the Wi-Fi network that the ESP32 will connect to.
WebServer server(80);
WebServer server(80): Creates an HTTP server instance that listens on port 80 (default HTTP port).
static auto hiRes = esp32cam::Resolution::find(800, 600);
esp32cam::Resolution::find: Defines camera resolutions:
hiRes: High resolution (800x600).
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
esp32cam::capture: Captures a frame from the camera.
Failure Handling: If no frame is captured, it logs a failure and sends a 503 error response.
Logging Success: Prints the resolution and size of the captured image.
Serving the Image:
Sets the content length and MIME type as image/jpeg.
Writes the image data directly to the client.
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
handleJpgHi: Switches the camera to high resolution using esp32cam::Camera.changeResolution(hiRes) and calls serveJpg.
Error Logging: If the resolution change fails, it logs a failure message to the Serial Monitor.
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
Serial Initialization:
Initializes the serial port for debugging.
Sets baud rate to 115200.
Camera Configuration:
Sets pins for the AI Thinker ESP32-CAM module.
Configures the default resolution, buffer count, and JPEG quality (80%).
Attempts to initialize the camera and log the status.
Wi-Fi Setup:
Connects to the specified Wi-Fi network in station mode.
Waits for the connection and logs the device's IP address.
Web Server Routes:
Maps URL endpoint ( /cam-hi.jpg).
Server Start:
Starts the web server.
void loop()
{
server.handleClient();
}
server.handleClient(): Continuously listens for incoming HTTP requests and serves responses based on the defined endpoints.
The ESP32-CAM connects to Wi-Fi and starts a web server.
URL endpoint /cam-hi.jpg) lets the user request images at high resolution.
The camera captures an image and serves it to the client as a JPEG.
The system continuously handles new client requests.
import cv2
import urllib.request
import numpy as np
import time
url = 'http://192.168.1.101/cam-hi.jpg'
detector = cv2.QRCodeDetector()
scanned_text = None
while True:
# Fetch frame from the IP camera URL
img_resp = urllib.request.urlopen(url)
img_arr = np.array(bytearray(img_resp.read()), dtype=np.uint8)
frame = cv2.imdecode(img_arr, -1)
if frame is None:
continue
# QR Code detection
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
decoded_text, points, _ = detector.detectAndDecode(gray)
if not decoded_text:
# If normal detection fails, try preprocessing
enhanced = cv2.GaussianBlur(gray, (5, 5), 0)
enhanced = cv2.adaptiveThreshold(enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
decoded_text, points, _ = detector.detectAndDecode(enhanced)
if points is not None and decoded_text:
if decoded_text != scanned_text:
print(f"Decoded: {decoded_text}")
scanned_text = decoded_text
# Convert points to integer values and draw the bounding box
points = points.astype(int) # Convert float points to integer
cv2.polylines(frame, [points], isClosed=True, color=(0, 255, 0), thickness=3)
# Display the frame with QR code detection
cv2.imshow("QR Scanner", frame)
# Wait for 'q' key to exit the loop
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
import cv2
import urllib.request
import numpy as np
import time
cv2 → OpenCV library for image processing.
urllib.request → Fetches the image frame from the ESP32-CAM URL.
numpy → Handles image data in arrays.
time → (Unused here but often used for timing/debugging).
url = 'http://192.168.1.101/cam-hi.jpg'
The ESP32-CAM provides a JPEG stream over this local IP address.
Ensure that your ESP32-CAM is connected to the same Wi-Fi network.
detector = cv2.QRCodeDetector()
cv2.QRCodeDetector() creates an instance of OpenCV's built-in QR code detector.
scanned_text = None
This stores the last detected QR code text.
Used to prevent duplicate prints of the same QR code.
while True:
Runs indefinitely to keep fetching frames and detecting QR codes.
img_resp = urllib.request.urlopen(url)
img_arr = np.array(bytearray(img_resp.read()), dtype=np.uint8)
frame = cv2.imdecode(img_arr, -1)
urllib.request.urlopen(url): Fetches the image as bytes.
bytearray(img_resp.read()): Converts the byte stream into an array.
np.array(..., dtype=np.uint8): Converts the byte array into a NumPy array (for image processing).
cv2.imdecode(img_arr, -1): Decodes the array into an OpenCV image (frame).
if frame is None:
continue
Ensures the loop does not crash if the frame is not properly retrieved.
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
Converts the frame to grayscale for better QR code detection.
QR code detection works better on grayscale images.
decoded_text, points, _ = detector.detectAndDecode(gray)
detectAndDecode(gray):
Detects QR code in the image.
Returns:
decoded_text → The text inside the QR code.
points → The four corner points of the QR code.
_ → A binary mask (not used here).
if not decoded_text:
enhanced = cv2.GaussianBlur(gray, (5, 5), 0)
enhanced = cv2.adaptiveThreshold(enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
decoded_text, points, _ = detector.detectAndDecode(enhanced)
If the first detection attempt fails, the script applies:
Gaussian Blur → Reduces noise.
Adaptive Thresholding → Enhances contrast.
Then, it retries QR code detection on the enhanced image.
if points is not None and decoded_text:
If a QR code is successfully detected, process it.
if decoded_text != scanned_text:
print(f"Decoded: {decoded_text}")
scanned_text = decoded_text
Ensures the script does not print the same QR code multiple times.
points = points.astype(int) # Convert float points to integer
cv2.polylines(frame, [points], isClosed=True, color=(0, 255, 0), thickness=3)
Converts points to integer values.
Uses cv2.polylines() to draw a green bounding box around the detected QR code.
cv2.imshow("QR Scanner", frame)
Opens a live OpenCV window displaying the video stream with QR detection.
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Waits 1 millisecond for a key press.
If the user presses 'q', the loop exits.
cv2.destroyAllWindows()
Closes all OpenCV windows and frees resources.
Run the Python code and place your camera in front of a QR code. The QR code will be detected inside a green bounding box.
Fig: QR code detected
You will see the decoded QR code in the output window.
And there you have it! We successfully built a real-time QR code scanner using an ESP32-CAM and OpenCV. The script continuously grabs frames from the ESP32-CAM’s live feed, detects QR codes, and even draws a bounding box around them. If the initial detection doesn’t work, it smartly enhances the image to improve accuracy.
This setup can be super handy for things like automated check-ins, inventory tracking, or even smart home projects. But this is just the beginning! You can take it even further by Storing scanned QR codes in a database, triggering automated actions based on the scanned data
and expanding it to multiple cameras for larger applications
With the power of computer vision and the flexibility of the ESP32-CAM, the possibilities are endless. So go ahead, experiment, tweak, and see where you can take it!
Hello, dear tech savvies! We hope everything is going fine with you. Today we’re back with another interesting project. Do you ever wonder how amazing it would be to have a text reader that would be able to read texts from pictures and videos? Think about a self-driving car that can read the road signs meticulously and go to the right direction. Or imagine an AI bot that can read what is written on images uploaded to social media. How nice it would be to have such a system that will be able to read vulgar posts and filter them even when they are in picture format? Or imagine a caregiver robot that can read the medicine bottle levels and give medicines to the patients always on time. Now you understand how important it is for AI solutions to recognize texts, right?
Today, we are going to do the same task in this project. The main component of our project is an ESP32-CAM. We will integrate it with the OpenCV library of Python. The Python code will read text from the video feed and show the text in the output terminal.
The ESP32-CAM is a powerful yet affordable development board that combines the ESP32 microcontroller with an integrated camera module, making it an excellent choice for IoT and vision-based applications. Whether you're building a wireless security camera, a QR code scanner, or an AI-powered image recognition system, the ESP32-CAM provides a compact and cost-effective solution.
One of its standout features is built-in WiFi and Bluetooth connectivity, allowing it to stream video or capture images remotely. Despite its small size, it packs a punch with a dual-core processor, support for microSD card storage, and compatibility with various camera sensors (such as the OV2640). However, since it lacks built-in USB-to-serial functionality, flashing firmware requires an external FTDI adapter.
This system consists of an ESP32-CAM module capturing images and serving them over a web server. A separate Python-based OpenCV application fetches the images, processes them for Optical Character Recognition (OCR) using EasyOCR, and displays the results.
ESP32-CAM Module
Captures images at 800x600 resolution.
Hosts a web server on port 80 to serve the images.
Connects to a Wi-Fi network as a station.
Provides image data when requested via an HTTP GET request.
Python OpenCV & EasyOCR Client
Requests images from the ESP32-CAM web server via HTTP GET requests.
Decodes the image and preprocesses it (resizing & grayscale conversion).
Performs OCR using EasyOCR.
Displays the real-time camera feed and extracted text.
The ESP32-CAM initializes and configures the camera settings.
It connects to the Wi-Fi network.
It starts an HTTP web server that serves JPEG images via the endpoint http://
When a request is received on /cam-hi.jpg, the ESP32-CAM captures an image and returns it as a response.
The Python script continuously fetches images from the ESP32-CAM.
The image is converted from a raw HTTP response into an OpenCV-compatible format.
It is resized to 400x300 for faster processing.
It is converted to grayscale to improve OCR accuracy.
EasyOCR processes the grayscale image to recognize text.
Detected text is printed to the console.
The processed image feed is displayed using OpenCV.
The user can view the real-time video feed.
The recognized text is displayed in the terminal.
The script can be terminated by pressing 'q'.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module |
1 |
FTDI USB to Serial Converter 3V3-5V |
1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
The following is the circuit diagram for this project:
Fig: Circuit diagram
ESP32-CAM WiFi + Bluetooth Camera Module |
FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
If this is your first project with an ESP32 board, you need to do board installation. You will also need to download and install the ESP32-CAM library. To make the camera functional, the cp210x USB driver and the FTDI driver must be properly installed on your computer. Here is a detailed tutorial that shows how to get started with the ESP32-CAM.
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WebServer server(80);
static auto hiRes = esp32cam::Resolution::find(800, 600);
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
void loop()
{
server.handleClient();
}
After uploading the code, disconnect the IO0 pin of the camera from GND. Then press the RST pin. The following messages will appear.
Fig: Code successfully uploaded to ESP32-CAM
You have to copy the IP address and paste it into the following part of your Python code.
Fig: Copy-pasting the URL to the Python script
#include
#include
#include
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WIFI_SSID and WIFI_PASS: Define the SSID and password of the Wi-Fi network that the ESP32 will connect to.
WebServer server(80);
WebServer server(80): Creates an HTTP server instance that listens on port 80 (default HTTP port).
static auto hiRes = esp32cam::Resolution::find(800, 600);
esp32cam::Resolution::find: Defines camera resolutions:
hiRes: High-resolution (800x600).
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
esp32cam::capture: Captures a frame from the camera.
Failure Handling: If no frame is captured, it logs a failure and sends a 503 error response.
Logging Success: Prints the resolution and size of the captured image.
Serving the Image:
Sets the content length and MIME type as image/jpeg.
Writes the image data directly to the client.
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
handleJpgHi: Switches the camera to high resolution using esp32cam::Camera.changeResolution(hiRes) and calls serveJpg.
Error Logging: If the resolution change fails, it logs a failure message to the Serial Monitor.
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
∙ Serial Initialization:
Initializes the serial port for debugging.
Sets baud rate to 115200.
∙ Camera Configuration:
Sets pins for the AI Thinker ESP32-CAM module.
Configures the default resolution, buffer count, and JPEG quality (80%).
Attempts to initialize the camera and log the status.
∙ Wi-Fi Setup:
Connects to the specified Wi-Fi network in station mode.
Waits for the connection and logs the device's IP address.
∙ Web Server Routes:
Maps URL endpoint ( /cam-hi.jpg).
∙ Server Start:
Starts the web server.
void loop()
{
server.handleClient();
}
server.handleClient(): Continuously listens for incoming HTTP requests and serves responses based on the defined endpoints.
Summary of Workflow
The ESP32-CAM connects to Wi-Fi and starts a web server.
URL endpoint /cam-hi.jpg) lets the user request images at high resolution.
The camera captures an image and serves it to the client as a JPEG.
The system continuously handles new client requests.
import cv2
import requests
import numpy as np
import easyocr
import time
# Replace with your ESP32-CAM IP
ESP32_CAM_URL = "http://192.168.1.101/cam-hi.jpg"
# Initialize EasyOCR reader
reader = easyocr.Reader(['en'], gpu=False)
def capture_image():
""" Captures an image from the ESP32-CAM """
try:
start_time = time.time()
response = requests.get(ESP32_CAM_URL, timeout=2) # Reduced timeout for faster response
if response.status_code == 200:
img_arr = np.frombuffer(response.content, np.uint8)
img = cv2.imdecode(img_arr, cv2.IMREAD_COLOR)
print(f"[INFO] Image received in {time.time() - start_time:.2f} seconds")
return img
else:
print("[Error] Failed to get image from ESP32-CAM.")
return None
except Exception as e:
print(f"[Error] {e}")
return None
print("[INFO] Starting text recognition...")
while True:
frame = capture_image()
if frame is None:
continue # Skip this iteration if the image wasn't retrieved
# Resize image for faster processing
frame_resized = cv2.resize(frame, (400, 300))
# Convert to grayscale (better OCR accuracy)
gray = cv2.cvtColor(frame_resized, cv2.COLOR_BGR2GRAY)
# Process image with EasyOCR
start_time = time.time()
results = reader.readtext(gray, detail=0, paragraph=True)
print(f"[INFO] OCR processed in {time.time() - start_time:.2f} seconds")
if results:
detected_text = " ".join(results)
print(f"[INFO] Recognized Text: {detected_text}")
# Display the image feed
cv2.imshow("ESP32-CAM Feed", frame_resized)
# Press 'q' to exit the loop
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Cleanup
cv2.destroyAllWindows()
This Python script captures images from an ESP32-CAM, processes them, and extracts text using EasyOCR. Below is a detailed breakdown of each part of the code.
import cv2 # OpenCV for image processing and display
import requests # To send HTTP requests to the ESP32-CAM
import numpy as np # NumPy for handling image arrays
import easyocr # EasyOCR for text recognition
import time # For measuring performance time
cv2 (OpenCV) → Used for decoding, processing, and displaying images.
requests → Fetches the image from the ESP32-CAM.
numpy → Converts the image data into a format usable by OpenCV.
easyocr → Runs Optical Character Recognition (OCR) on the image.
time → Measures execution time for optimization.
ESP32_CAM_URL = "http://192.168.1.100/cam-hi.jpg"
The ESP32-CAM hosts an image at this URL.
Ensure your ESP32-CAM and PC are on the same network.
reader = easyocr.Reader(['en'], gpu=False)
EasyOCR is initialized with English ('en') as the recognition language.
gpu=False ensures it runs on the CPU (Set gpu=True if using a GPU for faster processing).
def capture_image():
""" Captures an image from the ESP32-CAM """
try:
start_time = time.time()
response = requests.get(ESP32_CAM_URL, timeout=2) # Reduced timeout for faster response
Sends an HTTP GET request to fetch an image.
timeout=2 → Ensures it doesn’t wait too long (prevents network lag).
if response.status_code == 200:
img_arr = np.frombuffer(response.content, np.uint8)
img = cv2.imdecode(img_arr, cv2.IMREAD_COLOR)
print(f"[INFO] Image received in {time.time() - start_time:.2f} seconds")
return img
If HTTP response is successful (200 OK):
Convert raw binary data (response.content) into a NumPy array.
Use cv2.imdecode() to convert it into an OpenCV image.
Print how long the image retrieval took.
Return the image.
else:
print("[Error] Failed to get image from ESP32-CAM.")
return None
If the ESP32-CAM fails to respond, it prints an error message and returns None.
except Exception as e:
print(f"[Error] {e}")
return None
Handles connection errors (e.g., ESP32-CAM offline, network issues).
print("[INFO] Starting text recognition...")
Logs a message when the program starts.
while True:
frame = capture_image()
if frame is None:
continue # Skip this iteration if the image wasn't retrieved
Continuously fetch images from ESP32-CAM.
If None (failed to capture), skip processing and retry.
# Resize image for faster processing
frame_resized = cv2.resize(frame, (400, 300))
# Convert to grayscale (better OCR accuracy)
gray = cv2.cvtColor(frame_resized, cv2.COLOR_BGR2GRAY)
Resizing to (400, 300) → Speeds up OCR processing without losing clarity.
Converting to grayscale → Improves OCR accuracy.
start_time = time.time()
results = reader.readtext(gray, detail=0, paragraph=True)
print(f"[INFO] OCR processed in {time.time() - start_time:.2f} seconds")
Calls reader.readtext(gray, detail=0, paragraph=True).
detail=0 → Returns only the recognized text.
paragraph=True → Groups words into sentences.
Logs how long OCR processing takes.
if results:
detected_text = " ".join(results)
print(f"[INFO] Recognized Text: {detected_text}")
If text is detected, print the recognized text.
cv2.imshow("ESP32-CAM Feed", frame_resized)
Opens a real-time preview window of the ESP32-CAM feed.
# Press 'q' to exit the loop
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Press 'q' to exit the loop and stop the program.
cv2.destroyAllWindows()
Closes all OpenCV windows when the program exits.
Create a virtual environment:
python -m venv ocr_env
source ocr_env/bin/activate # Linux/Mac
ocr_env\Scripts\activate # Windows
Install required libraries:
pip install opencv-python numpy easyocr requests
After setting up the Python environment, run the Python code to capture images from the ESP32-CAM and perform text recognition using EasyOCR.
Run the Python code and place your camera in front of a text. The text will be detected.
Fig: Sample
You will see the text in the output window.
Fig: Detected text shown
fig: sample
fig: Detected text
Congratulations! You've successfully built a real-time OCR system using ESP32-CAM and Python. With this setup, your ESP32-CAM captures images and streams them to your Python script, where OpenCV and EasyOCR extract text from the visuals. Whether you're automating data entry, reading license plates, or enhancing accessibility, this project lays the foundation for countless applications.
Now that you have it running, why not take it a step further? You could improve accuracy with better lighting, add pre-processing filters, or even integrate the results into a database or web dashboard. The possibilities are endless!
If you run into any issues or have ideas for improvements, feel free to experiment, tweak the code, and keep learning. Happy coding!
Hello friends. We hope you are doing fine. The world is full of colours. Isn’t it? We humans can see and differentiate the colours very easily. But teaching robots and AI apps about colours is a real challenge. With the advancement of computer vision and embedded systems, this task has become easier than before. Today, we are going to make an RGB colour identifier using the ESP32-CAM. This project combines the power of OpenCV with the ESP32-CAM module to create a simple but effective system for detecting and tracking basic colors in real time.
This system consists of an ESP32-CAM module acting as a live-streaming camera server and a Python-based computer vision application running on a remote computer. The Python application fetches images from the ESP32-CAM, processes them using OpenCV, and detects objects of specific colours (red, green, and blue) based on HSV filtering.
ESP32-CAM (AI Thinker module)
Captures images in JPEG format.
Streams images over WiFi using a built-in web server.
WiFi Router/Network
Connects ESP32-CAM and the processing computer.
Processing Computer (Laptop/Desktop/Raspberry Pi)
Runs Python with OpenCV to process images from ESP32-CAM.
Performs colour detection and contour analysis.
ESP32-CAM Firmware (Arduino Code)
Uses the esp32cam library for camera control.
Uses WiFi.h for network connectivity.
Uses WebServer.h to create an HTTP server.
Captures and serves images at http://
Python OpenCV Script (Color Detection Algorithm)
Fetches images from ESP32-CAM via urllib.request.
Converts images to HSV format for color-based segmentation.
Detects red, green, and blue objects using defined HSV thresholds.
Draws bounding contours and labels detected colours.
Displays processed video frames with detected objects.
ESP32-CAM connects to WiFi.
Sets up a web server to serve captured images at http://
The camera captures images in JPEG format (800x600 resolution).
Stores and serves the latest frame via an HTTP endpoint.
The Python script sends a request to ESP32-CAM to get the latest image frame.
The image is received in JPEG format and decoded using OpenCV.
Converts the image from BGR to HSV.
Applies thresholding masks to detect red, green, and blue objects.
Extracts contours of detected objects.
Filters out small objects using an area threshold (>2000 pixels).
Computes the centroid of detected objects.
Draws bounding contours and labels detected objects.
Shows the original frame with detected objects and labels.
Pressing 'q' stops execution and closes all OpenCV windows.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module |
1 |
FTDI USB to Serial Converter 3V3-5V |
1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
The following is the circuit diagram for this project:
Fig: Circuit diagram
ESP32-CAM WiFi + Bluetooth Camera Module |
FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
If it is your first project with any board of the ESP32 series, this part of the tutorial is for you. you need to do the board installation. You may also need to install the CP210x USB driver. If ESP32 boards are already installed in your Arduino IDE, you can skip this installation section. Go to File > preferences, type https://dl.espressif.com/dl/package_esp32_index.json and click OK.
Fig: Board Installation
Go to Tools>Board>Boards Manager and install the ESP32 boards.
Fig: Board Installation
Download the ESP32-CAM library from Github (the link is given in the reference section). Then install it by following the path sketch>include library> add.zip library.
Now select the correct path to the library, click on the library folder and press open.
Connect the camera board to your computer. Some camera boards come with a micro USB connector of their own. You can connect the camera to the computer by using a micro USB data cable. If the board has no connector, you have to connect the FTDI module to the computer with the data cable. If you never used the FTDI board on your computer, you will need to install the FTDI driver first.
After connecting the camera, Go to Tools>boards>esp32>Ai thinker ESP32-CAM
Fig: Camera board selection
After selecting the board, select the appropriate COM port and upload the following code:
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WebServer server(80);
static auto hiRes = esp32cam::Resolution::find(800, 600);
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
void loop()
{
server.handleClient();
}
After uploading the code, disconnect the IO0 pin of the camera from GND. Then press the RST pin. The following messages will appear.
Fig: Code successfully uploaded to ESP32-CAM
You have to copy the IP address and paste it into the following part of your Python code.
Copy-paste the following Python code and save it using a Python interpreter.
import cv2
import urllib.request
import numpy as np
def nothing(x):
pass
url = 'http://192.168.1.108/cam-hi.jpg'
cv2.namedWindow("live transmission", cv2.WINDOW_AUTOSIZE)
# Red, Green, and Blue HSV ranges
red_lower1 = np.array([0, 120, 70])
red_upper1 = np.array([10, 255, 255])
red_lower2 = np.array([170, 120, 70])
red_upper2 = np.array([180, 255, 255])
green_lower = np.array([40, 70, 70])
green_upper = np.array([80, 255, 255])
blue_lower = np.array([90, 70, 70])
blue_upper = np.array([130, 255, 255])
while True:
img_resp = urllib.request.urlopen(url)
imgnp = np.array(bytearray(img_resp.read()), dtype=np.uint8)
frame = cv2.imdecode(imgnp, -1)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Create masks for Red, Green, and Blue
mask_red1 = cv2.inRange(hsv, red_lower1, red_upper1)
mask_red2 = cv2.inRange(hsv, red_lower2, red_upper2)
mask_red = cv2.bitwise_or(mask_red1, mask_red2)
mask_green = cv2.inRange(hsv, green_lower, green_upper)
mask_blue = cv2.inRange(hsv, blue_lower, blue_upper)
# Find contours for each color independently
for color, mask, lower, upper in [("red", mask_red, red_lower1, red_upper1),
("green", mask_green, green_lower, green_upper),
("blue", mask_blue, blue_lower, blue_upper)]:
cnts, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
area = cv2.contourArea(c)
if area > 2000: # Only consider large contours
# Get contour center
M = cv2.moments(c)
if M["m00"] != 0: # Avoid division by zero
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# Draw contours and color label
cv2.drawContours(frame, [c], -1, (255, 0, 0), 3) # Draw contour in blue
cv2.circle(frame, (cx, cy), 7, (255, 255, 255), -1) # Draw center circle
cv2.putText(frame, color, (cx - 20, cy - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
res = cv2.bitwise_and(frame, frame, mask=mask_red) # Show result with red mask
cv2.imshow("live transmission", frame)
cv2.imshow("res", res)
key = cv2.waitKey(5)
if key == ord('q'):
break
cv2.destroyAllWindows()
1)Create a virtual environment:
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
2)Install required libraries:
pip install opencv-python numpy
pip install urllib3
After setting the Pythong Environment, run the Python code.
#include
#include
#include
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WIFI_SSID and WIFI_PASS: Define the SSID and password of the Wi-Fi network that the ESP32 will connect to.
WebServer server(80);
WebServer server(80): Creates an HTTP server instance that listens on port 80 (default HTTP port).
static auto hiRes = esp32cam::Resolution::find(800, 600);
esp32cam::Resolution::find: Defines camera resolutions:
hiRes: High resolution (800x600).
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
esp32cam::capture: Captures a frame from the camera.
Failure Handling: If no frame is captured, it logs a failure and sends a 503 error response.
Logging Success: Prints the resolution and size of the captured image.
Serving the Image:
Sets the content length and MIME type as image/jpeg.
Writes the image data directly to the client.
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
handleJpgHi: Switches the camera to high resolution using esp32cam::Camera.changeResolution(hiRes) and calls serveJpg.
Error Logging: If the resolution change fails, it logs a failure message to the Serial Monitor.
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
Serial Initialization:
Initializes the serial port for debugging.
Sets baud rate to 115200.
Camera Configuration:
Sets pins for the AI Thinker ESP32-CAM module.
Configures the default resolution, buffer count, and JPEG quality (80%).
Attempts to initialize the camera and log the status.
Wi-Fi Setup:
Connects to the specified Wi-Fi network in station mode.
Waits for the connection and logs the device's IP address.
Web Server Routes:
Maps URL endpoint ( /cam-hi.jpg).
Server Start:
Starts the web server.
void loop()
{
server.handleClient();
}
server.handleClient(): Continuously listens for incoming HTTP requests and serves responses based on the defined endpoints.
The ESP32-CAM connects to Wi-Fi and starts a web server.
URL endpoint /cam-hi.jpg) lets the user request images at high resolution.
The camera captures an image and serves it to the client as a JPEG.
The system continuously handles new client requests.
This code captures images from a live video stream over the network, processes them to detect red, green, and blue regions, and highlights these regions on the video feed.
cv2 (OpenCV):
Used for image and video processing, including reading, decoding, and displaying images.
urllib.request:
Handles HTTP requests to fetch the video feed from the given URL.
numpy:
Handles array operations, which are used for creating HSV ranges and masks.
Purpose: A placeholder function that does nothing. Typically used for trackbar callbacks in OpenCV.
Usage in Code: It's defined but not used in this snippet.
Stores the URL of the live video feed (http://192.168.1.106/cam-hi.jpg).
Red: Two HSV ranges for red, as red wraps around the HSV hue space (0–10 and 170–180 degrees).
Green: HSV range for green (40–80 degrees).
Blue: HSV range for blue (90–130 degrees).
Creates a window named "live transmission" for displaying the processed video feed.
cv2.WINDOW_AUTOSIZE: Ensures the window size adjusts automatically based on the image size.
Fetch Image:
img_resp = urllib.request.urlopen(url)
imgnp = np.array(bytearray(img_resp.read()), dtype=np.uint8)
frame = cv2.imdecode(imgnp, -1)
urllib.request.urlopen(url): Opens the URL and fetches the image bytes.
bytearray(img_resp.read()): Converts the response data to a byte array.
np.array(..., dtype=np.uint8): Converts the byte array into a NumPy array.
cv2.imdecode(imgnp, -1): Decodes the NumPy array into an image (frame).
Convert to HSV:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
Converts the image from BGR to HSV color space, which makes color detection easier.
Create Color Masks:
mask_red1 = cv2.inRange(hsv, red_lower1, red_upper1)
mask_red2 = cv2.inRange(hsv, red_lower2, red_upper2)
mask_red = cv2.bitwise_or(mask_red1, mask_red2)
mask_green = cv2.inRange(hsv, green_lower, green_upper)
mask_blue = cv2.inRange(hsv, blue_lower, blue_upper)
cv2.inRange(hsv, lower, upper): Creates a binary mask where pixels in the HSV range are white (255) and others are black (0).
Combines two masks for red (since red spans two HSV ranges).
Creates masks for green and blue.
Find and Process Contours:
cnts, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.findContours:
Finds contours (boundaries of white regions) in the binary mask.
cv2.RETR_TREE: Retrieves all contours and reconstructs a full hierarchy.
cv2.CHAIN_APPROX_SIMPLE: Compresses horizontal, vertical, and diagonal segments to save memory.
Contour Processing:
for c in cnts:
area = cv2.contourArea(c)
if area > 2000: # Only consider large contours
M = cv2.moments(c)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
cv2.drawContours(frame, [c], -1, (255, 0, 0), 3)
cv2.circle(frame, (cx, cy), 7, (255, 255, 255), -1)
cv2.putText(frame, color, (cx - 20, cy - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255),
cv2.contourArea(c): Calculates the area of the contour.
Threshold: Only processes contours with an area > 2000 to ignore noise.
Moments: Used to calculate the centre of the contour (cx, cy).
Drawing:
cv2.drawContours: It draws the contour in blue.
cv2.circle: It draws a white circle at the center.
cv2.putText: Labels the contour with its colour name.
Display the Results:
res = cv2.bitwise_and(frame, frame, mask=mask_red)
cv2.imshow("live transmission", frame)
cv2.imshow("res", res)
cv2.bitwise_and: Applies the red mask to the original frame, keeping only the red regions visible.
cv2.imshow: Displays the processed video feed in two windows:
"live transmission" shows the annotated frame.
"res" shows only the red regions.
Exit Condition:
key = cv2.waitKey(5)
if key == ord('q'):
break
cv2.waitKey(5): Waits for 5 ms for a key press.
Exit Key: If 'q' is pressed, the loop breaks.
cv2.destroyAllWindows()
Closes all OpenCV windows after exiting the loop.
This script continuously fetches images from a network camera, processes them to detect red, green, and blue regions, and overlays visual markers and labels on the detected regions. It is a real-time colour detection and visualization application with a clear exit mechanism.
Power up the ESP32-CAM and connect it to Wi-Fi.
Run the Python script. Make sure that the ESP32-CAM URL is correctly set.
Test with Red, Green and Blue objects. You have to place the objects in front of the ESP32-CAM.
Fig: Green detected
Fig: Red and blue detected
Fig: Blue detected
Guru Meditation Error: Ensure stable power to the ESP32-CAM.
No Image Display: You probably entered the wrong IP address! Check the IP address and ensure the ESP32-CAM is accessible from your computer.
Library Conflicts: Use a virtual environment to isolate Python dependencies.
Dots when uploading the code: Immediately press the RST button.
Multiple failed upload attempts despite pressing the RST button: Restart your computer and try again.
By integrating ESP32 and OpenCV, we have made a basic RGB colour identifier in this project. We can use this to make apps for colour-blind people. Depending on colours, industrial control systems often need to sort products and raw materials. This project can be integrated with such sorting systems. Colour detection is also important for humanoid robots. Our project can be integrated with humanoid robots to add that feature. The code can be further fine-tuned to identify more colours.
Hello friends. We hope you are doing fine. Today we are back with another interesting project. It is based on the image processing technology. Developing efficient and cost-effective solutions for real-time applications is becoming increasingly important in the area of embedded systems and computer vision. This project makes full use of ESP32-CAM. ESP32-CAM is a compact and AI-enabled microcontroller with built-in Wi-Fi capabilities. We will create a real-time face detection and counting system.
The ESP32-CAM serves as the core of the system. It captures high-resolution images at 800x600 resolution and hosts an HTTP server to serve individual JPEG images over a local network. The device’s efficient JPEG compression and network capabilities ensure minimal latency while maintaining high-quality image delivery, enabling real-time processing on the client side.
On the client side, a Python application powered by OpenCV collects image frames from the ESP32-CAM. Using Haar cascade classifiers, the application detects faces in each frame. It can also figure out whether they are frontal or in profile orientation.
This project is focused on face detection and counting. It marks detected faces with bounding boxes. It also counts both frontal and profile faces seen in the video stream.
Applications of this face detection and counting system include smart attendance systems, people flow monitoring in public spaces, and automation solutions in retail or event management. This project demonstrates how IoT-enabled devices like the ESP32-CAM can work seamlessly with computer vision algorithms to provide cost-effective and reliable solutions for real-world challenges. By focusing solely on face detection and counting, the system achieves an optimal balance between simplicity, scalability, and computational efficiency.
ESP32-CAM:
Captures images at a resolution of 800x600 (or specified resolution).
Serves captured images over an HTTP server at a specific endpoint (e.g., /cam-hi.jpg).
Configured to operate as an access point or station mode connected to Wi-Fi.
Network Connection:
Wi-Fi provides communication between the ESP32-CAM and the Python application running on a computer.
Computer:
Runs the Python application to process the images and display results.
ESP32-CAM Firmware:
Configures the camera for capturing images.
Sets up a lightweight HTTP server to serve JPEG images to connected clients.
Python Application:
Fetches images from the ESP32-CAM.
Processes images to count and annotate detected faces.
HTTP Protocol:
The ESP32-CAM serves images using HTTP.
The Python application uses HTTP GET requests to fetch the images from the camera.
Image Acquisition:
Python fetches images from the ESP32-CAM endpoint.
Preprocessing:
Converts the fetched image to a format suitable for OpenCV operations (e.g., cv2.imdecode to convert byte data into an image).
Face Detection:
Uses OpenCV's Haar Cascade classifiers to detect:
Frontal Faces: Uses haarcascade_frontalface_default.xml.
Profile Faces: Uses haarcascade_profileface.xml.
Counts the number of faces detected in the current frame.
Annotation:
Draws bounding boxes (rectangles) and labels around detected faces on the image frame.
Adds text overlays to display the count of detected frontal and profile faces.
Visual Output:
Displays the annotated frames with bounding boxes and face counts in a real-time OpenCV window titled "Face Detector."
User Interaction:
Allows the user to terminate the application by pressing the 'q' key.
Image Capture:
ESP32-CAM captures and serves the image.
Image Fetching:
Python retrieves the image via an HTTP GET request.
Processing and Detection:
Haar Cascade classifiers detect faces, count them, and annotate the frame.
Display and Output:
Python displays the processed image in a GUI window with visual feedback for face counts.
Loop and Termination:
The loop continues until the user exits.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module |
1 |
FTDI USB to Serial Converter 3V3-5V |
1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
The following is the circuit diagram for this project.
Fig: Circuit diagram
ESP32-CAM WiFi + Bluetooth Camera Module |
FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
If it is your first project with any board of the ESP32 series, you need to do the board installation first. If ESP32 boards are already installed in your Arduino IDE, you can skip this installation section. You may also need to install the CP210x USB driver.
Go to File > preferences, type https://dl.espressif.com/dl/package_esp32_index.json and click OK.
Fig: Board Installation
Go to Tools>Board>Boards Manager and install the ESP32 boards.
Fig: Board Installation
Download the ESP32-CAM library from Github (the link is given in the reference section). Then install it by following the path sketch>include library> add.zip library.
Now select the correct path to the library, click on the library folder and press open.
Connect the camera board to your computer. Some camera boards come with a micro USB connector of their own. You can connect the camera to the computer by using a micro USB data cable. If the board has no connector, you have to connect the FTDI module to the computer with the data cable. If you never used the FTDI board on your computer, you will need to install the FTDI driver first.
After connecting the camera, Go to Tools>boards>esp32>Ai thinker ESP32-CAM
Fig: Camera board selection
After selecting the board, select the appropriate COM port and upload the following code:
#include
#include
#include
const char* WIFI_SSID = "Hamad";
const char* WIFI_PASS = "barsha123";
WebServer server(80);
static auto hiRes = esp32cam::Resolution::find(800, 600);
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
void loop()
{
server.handleClient();
}
After uploading the code, disconnect the IO0 pin of the camera from GND. Then press the RST pin. The following messages will appear.
Fig: Code successfully uploaded to ESP32-CAM
You have to copy the IP address and paste it into the following part of your Python code.
Face detection in this project relies on pre-trained Haar cascade models provided by OpenCV. These models are essential for detecting features like frontal and profile faces in images. Haar cascades are XML files containing trained data for specific object detection tasks. For this project, the following models are used:
Frontal Face Detection Model: haarcascade_frontalface_default.xml
Profile Face Detection Model: haarcascade_profileface.xml
These files are mandatory for the Python code to perform face detection. Below is a guide on how to download and set up these files.
The Haar cascade models can be downloaded directly from OpenCV’s GitHub repository.
Open your web browser and go to the OpenCV GitHub repository for Haar cascades:
https://github.com/opencv/opencv/tree/master/data/haarcascades
Locate the following files in the repository:
haarcascade_frontalface_default.xml
haarcascade_profileface.xml
Click on each file to open its content.
On the file's page, click the Raw button to view the raw XML content.
Right-click and select Save As to download the file. Save it with its original filename (.xml extension) to the directory where your Python script (main.py) is saved.
Since the XML files are placed in the same directory as your Python script, there is no need to specify a separate folder in your code. Ensure the downloaded files are saved in the same directory as your script, as shown below:
project_folder/
├── main.py
├── haarcascade_frontalface_default.xml
└── haarcascade_profileface.xml
Update your script to load the models from the current directory. This requires referencing the XML files directly without a folder path:
frontal_face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
profile_face_cascade = cv2.CascadeClassifier("haarcascade_profileface.xml")
Ensure the XML files are saved in the same directory as the Python script.
Run the Python script. If the models load successfully, there will be no errors related to file loading, and face detection should function as expected.
By downloading the files and placing them in the same directory as your script, you simplify the setup and enable seamless face detection functionality.
Copy-paste the following Python code and save it using a Python interpreter.
import cv2
import requests
import numpy as np
# Replace with your ESP32-CAM's IP address
ESP32_CAM_URL = "http://192.168.1.104/cam-hi.jpg"
# Load Haar Cascades for different types of face detection
frontal_face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
profile_face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
def process_frame(frame):
# Convert to grayscale for detection
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Perform frontal face detection
frontal_faces = frontal_face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(20, 20))
# Perform profile face detection
profile_faces = profile_face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(20, 20))
# Draw rectangles for detected frontal faces
for (x, y, w, h) in frontal_faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2) # Red for frontal faces
cv2.putText(frame, "Frontal Face", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
# Draw rectangles for detected profile faces
for (x, y, w, h) in profile_faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) # Blue for profile faces
cv2.putText(frame, "Profile Face", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
# Add detection counts to the frame
cv2.putText(frame, f"Frontal Faces: {len(frontal_faces)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, f"Profile Faces: {len(profile_faces)}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
return frame
while True:
# Fetch an image from the ESP32-CAM
response = requests.get(ESP32_CAM_URL)
if response.status_code == 200:
img_arr = np.asarray(bytearray(response.content), dtype=np.uint8)
frame = cv2.imdecode(img_arr, cv2.IMREAD_COLOR)
# Process and display the frame
processed_frame = process_frame(frame)
cv2.imshow("Face Detector", processed_frame)
# Quit when 'q' is pressed
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
print("Failed to fetch image from ESP32-CAM")
cv2.destroyAllWindows()
1)Create a virtual environment:
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
2)Install required libraries:
pip install opencv-python numpy
After setting the Pythong Environment, run the Python code.
#include
#include
#include
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WIFI_SSID and WIFI_PASS: Define the SSID and password of the Wi-Fi network that the ESP32 will connect to.
WebServer server(80);
WebServer server(80): Creates an HTTP server instance that listens on port 80 (default HTTP port).
static auto hiRes = esp32cam::Resolution::find(800, 600);
esp32cam::Resolution::find: Defines camera resolutions:
hiRes: High resolution (800x600).
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
esp32cam::capture: Captures a frame from the camera.
Failure Handling: If no frame is captured, it logs a failure and sends a 503 error response.
Logging Success: Prints the resolution and size of the captured image.
Serving the Image:
Sets the content length and MIME type as image/jpeg.
Writes the image data directly to the client.
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
handleJpgHi: Switches the camera to high resolution using esp32cam::Camera.changeResolution(hiRes) and calls serveJpg.
Error Logging: If the resolution change fails, it logs a failure message to the Serial Monitor.
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-hi.jpg");
server.on("/cam-hi.jpg", handleJpgHi);
server.begin();
}
Serial Initialization:
Initializes the serial port for debugging.
Sets baud rate to 115200.
Camera Configuration:
Sets pins for the AI Thinker ESP32-CAM module.
Configures the default resolution, buffer count, and JPEG quality (80%).
Attempts to initialize the camera and logs the status.
Wi-Fi Setup:
Connects to the specified Wi-Fi network in station mode.
Waits for the connection and logs the device's IP address.
Web Server Routes:
Maps URL endpoint ( /cam-hi.jpg).
Server Start:
Starts the web server.
void loop()
{
server.handleClient();
}
server.handleClient(): Continuously listens for incoming HTTP requests and serves responses based on the defined endpoints.
The ESP32-CAM connects to Wi-Fi and starts a web server.
URL endpoint /cam-hi.jpg) lets the user request images at high resolution.
The camera captures an image and serves it to the client as a JPEG.
The system continuously handles new client requests.
import cv2
import requests
import numpy as np
cv2: OpenCV library for image processing.
requests: To fetch the image frames from the ESP32-CAM over HTTP.
numpy (np): For array operations, used here to handle the byte stream received from the ESP32-CAM.
ESP32_CAM_URL = "http://192.168.1.104/cam-hi.jpg"
Replace this URL with the actual IP address of your ESP32-CAM on your local network. The endpoint "/cam-hi.jpg" returns the latest frame captured by the ESP32-CAM.
frontal_face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
profile_face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
Haar cascades are pre-trained classifiers provided by OpenCV to detect objects like faces.
haarcascade_frontalface_default.xml: Detects frontal faces.
haarcascade_profileface.xml: Detects side/profile faces.
def process_frame(frame):
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY): Converts the image to grayscale, which is required by Haar cascades for face detection.
frontal_faces = frontal_face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(20, 20))
detectMultiScale: Detects objects in the image.
scaleFactor=1.1: Specifies how much the image size is reduced at each scale.
minNeighbors=5: Minimum number of neighbouring rectangles required for positive detection.
minSize=(20, 20): Minimum size of detected objects.
profile_faces = profile_face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(20, 20))
Same as frontal detection but uses the profile cascade for side faces.
for (x, y, w, h) in frontal_faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(frame, "Frontal Face", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
Draws a red rectangle around each detected frontal face.
Adds the label "Frontal Face" above the rectangle.
for (x, y, w, h) in profile_faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.putText(frame, "Profile Face", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
Draws a blue rectangle for each detected profile face.
Labels it as "Profile Face."
cv2.putText(frame, f"Frontal Faces: {len(frontal_faces)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, f"Profile Faces: {len(profile_faces)}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
Displays the count of detected frontal and profile faces on the top-left of the frame.
while True:
response = requests.get(ESP32_CAM_URL)
Continuously fetches images from the ESP32-CAM.
if response.status_code == 200:
img_arr = np.asarray(bytearray(response.content), dtype=np.uint8)
frame = cv2.imdecode(img_arr, cv2.IMREAD_COLOR)
Converts the HTTP response to a NumPy array.
Decodes the byte array into an OpenCV image using cv2.imdecode.
processed_frame = process_frame(frame)
cv2.imshow("Face Detector", processed_frame)
Processes the frame using the process_frame function.
Displays the processed frame in a window titled "Face Detector."
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Checks if the 'q' key is pressed to exit the loop.
else:
print("Failed to fetch image from ESP32-CAM")
Prints an error message if the ESP32-CAM fails to provide an image.
Clean Up
cv2.destroyAllWindows()
Closes all OpenCV windows when the program exits.
Setup:
The code connects to the ESP32-CAM via its IP address to fetch image frames in real time.
It loads pre-trained Haar Cascade classifiers for detecting frontal and profile faces.
Continuous Image Fetching:
The program enters a loop where it fetches a new image frame from the ESP32-CAM using an HTTP GET request.
Image Processing:
The image is converted into a format usable by OpenCV.
The frame is processed to:
Convert it to grayscale (required for Haar Cascade detection).
Detect frontal faces and profile faces using the respective classifiers.
Face Detection and Visualization:
For each detected face:
A rectangle is drawn around it:
Red for frontal faces.
Blue for profile faces.
A label ("Frontal Face" or "Profile Face") is added above the rectangle.
The count of detected frontal and profile faces is displayed on the frame.
Display:
The processed frame, with visual indicators and counts, is displayed in a window titled "Face Detector."
User Interaction:
The program continues fetching, processing, and displaying frames until the user presses the 'q' key to quit.
Error Handling:
If the ESP32-CAM fails to provide an image, an error message is printed, and the loop continues.
Cleanup:
Upon exiting the loop, all OpenCV windows are closed to release resources.
Fetch Image → 2. Convert Image → 3. Detect Faces → 4. Annotate Frame → 5. Display Frame → 6. Repeat Until Exit.
Power up the ESP32-CAM and connect it to Wi-Fi.
Run the Python script. Make sure that the ESP32-CAM URL is correctly set.
See the result of counting the faces in the display.
You can test with real-life people and photos.
Fig: Face counting
Guru Meditation Error: Ensure stable power to the ESP32-CAM.
No Image Display: Check the IP address and ensure the ESP32-CAM is accessible from your computer.
Library Conflicts: Use a virtual environment to isolate Python dependencies.
Dots at the time of uploading the code: Immediately press the RST button.
Multiple failed upload attempts despite pressing the RST button: Restart your computer and try again.
This project demonstrates an effective implementation of a face-counting system using ESP32-CAM and Python. The system uses the ESP32-CAM’s capability to capture and serve high-resolution images over HTTP. The Python client uses OpenCV's Haar cascade classifiers to effectively detect and count frontal and profile faces in each frame. It provides real-time feedback.
This project can be adapted for various applications, such as crowd monitoring, security, and smart building management. It provides an affordable and flexible solution.
Future improvements can be made using advanced face detection algorithms like DNN-based models. This project highlights how simple hardware and software integration can address complex problems in computer vision.
Imagine a real-time object counting system that is budget-friendly and easy to implement. You can achieve this goal with an ESP32-CAM. Today we will build an ESP32-CAM Object Counting System. This project is a combination of the power of embedded systems and computer vision.
The main processor of the system is ESP32-CAM, a budget-friendly microcontroller with an integrated camera. This tiny powerhouse captures live video streams and transmits them over Wi-Fi. On the other side, a Python-based application processes these streams, detects objects using image processing techniques, and displays the count dynamically.
Whether it’s tracking inventory in a warehouse, monitoring traffic flow, or automating production lines, this system is versatile and adaptable. You can implement this project with a minimum number of components. It is quite easy.
Join us as we explore how to build this smart counting system step-by-step. You'll learn to configure the ESP32-CAM, process images in Python, and create a seamless, real-time object detection system. Let’s see how to bring this project to life!
The ESP32-CAM Object Counting System is built on a modular and efficient architecture, combining hardware and software components to achieve real-time object detection and counting. Below is a detailed breakdown of the system architecture:
Acts as the primary hardware for image capture and Wi-Fi communication.
Equipped with an onboard camera to stream live video at different resolutions.
Connects to a local Wi-Fi network to transmit data.
Provides stable power to the ESP32-CAM module, typically via a USB connection or external battery pack.
The ESP32-CAM connects to a local Wi-Fi network to enable seamless data transmission.
Uses HTTP requests to serve video streams at different resolutions.
Runs a lightweight web server on port 80.
Responds to specific endpoints (/cam-lo.jpg, /cam-mid.jpg, /cam-hi.jpg) to provide real-time image frames at requested resolutions.
Captures and processes raw image data using the onboard camera.
Serves the images as JPEG streams through the HTTP server.
Receives image streams from the ESP32-CAM using HTTP requests.
Processes the images using OpenCV for:
Grayscale conversion.
Noise reduction with Gaussian blur.
Edge detection by using the Canny algorithm.
Contour detection to identify objects in the frame.
Counts the detected objects and updates the display dynamically.
Displays the real-time video stream with contours drawn around detected objects.
Provides a dynamic count of detected objects in the video feed.
The count is displayed on the console or integrated into a graphical interface.
Enables interaction through keyboard inputs (e.g., pressing 'a' to print the object count or 'q' to quit the application).
The ESP32-CAM captures live video and streams it as JPEG images over a Wi-Fi network.
The Python application on the host machine fetches the image frames via HTTP requests.
The fetched images undergo processing in OpenCV to detect and count objects.
The processed video is displayed, and the object count is dynamically updated based on user input.
This architecture ensures a clear separation of tasks, with the ESP32-CAM handling image capture and streaming, and the Python application focusing on image processing and visualization. The modular design makes it easy to expand or adapt the system for various applications.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module |
1 |
FTDI USB to Serial Converter 3V3-5V |
1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
The following is the circuit diagram for this project.
Fig: Circuit diagram
ESP32-CAM WiFi + Bluetooth Camera Module |
FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
If it is your first project with any board of the ESP32 series, you need to do the board installation first. If ESP32 boards are already installed in your Arduino IDE, you can skip this installation section. You may also need to install the CP210x USB driver.
Go to File > preferences, type https://dl.espressif.com/dl/package_esp32_index.json and click OK.
Fig: Board Installation
Go to Tools>Board>Boards Manager and install the ESP32 boards.
Fig: Board Installation
Download the ESP32-CAM library from Github (the link is given in the reference section). Then install it by following the path sketch>include library> add.zip library.
Now select the correct path to the library, click on the library folder and press open.
Connect the camera board to your computer. Some camera boards come with a micro USB connector of their own. You can connect the camera to the computer by using a micro USB data cable. If the board has no connector, you have to connect the FTDI module to the computer with the data cable. If you never used the FTDI board on your computer, you will need to install the FTDI driver first.
After connecting the camera, Go to Tools>boards>esp32>Ai thinker ESP32-CAM
Fig: Camera board selection
After selecting the board, select the appropriate COM port and upload the following code:
#include
#include
#include
const char* WIFI_SSID = "Hamad";
const char* WIFI_PASS = "barsha123";
WebServer server(80);
static auto loRes = esp32cam::Resolution::find(320, 240);
static auto midRes = esp32cam::Resolution::find(350, 530);
static auto hiRes = esp32cam::Resolution::find(800, 600);
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgLo()
{
if (!esp32cam::Camera.changeResolution(loRes)) {
Serial.println("SET-LO-RES FAIL");
}
serveJpg();
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void handleJpgMid()
{
if (!esp32cam::Camera.changeResolution(midRes)) {
Serial.println("SET-MID-RES FAIL");
}
serveJpg();
}
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-lo.jpg");
Serial.println(" /cam-hi.jpg");
Serial.println(" /cam-mid.jpg");
server.on("/cam-lo.jpg", handleJpgLo);
server.on("/cam-hi.jpg", handleJpgHi);
server.on("/cam-mid.jpg", handleJpgMid);
server.begin();
}
void loop()
{
server.handleClient();
}
After uploading the code, disconnect the IO0 pin of the camera from GND. Then press the RST pin. The following messages will appear.
Fig: Code successfully uploaded to ESP32-CAM
You have to copy the IP address and paste it into the following part of your Python code.
Copy-paste the following Python code and save it using a Python interpreter.
import cv2
import urllib.request
import numpy as np
url = 'http://192.168.1.101/' # Update the URL if needed
cv2.namedWindow("live transmission", cv2.WINDOW_AUTOSIZE)
while True:
img_resp = urllib.request.urlopen(url + 'cam-lo.jpg')
imgnp = np.array(bytearray(img_resp.read()), dtype=np.uint8)
img = cv2.imdecode(imgnp, -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(cv2.GaussianBlur(gray, (11, 11), 0), 30, 150, 3)
dilated = cv2.dilate(canny, (1, 1), iterations=2)
(Cnt, _) = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Draw contours
cv2.drawContours(img, Cnt, -1, (0, 255, 0), 2)
# Display the number of counted objects on the video feed
count_text = f"Objects Counted: {len(Cnt)}"
cv2.putText(img, count_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.imshow("live transmission", img)
cv2.imshow("mit contour", canny)
key = cv2.waitKey(5)
if key == ord('q'):
break
cv2.destroyAllWindows()
1)Create a virtual environment:
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
2)Install required libraries:
pip install opencv-python numpy
After setting the Pythong Environment, run the Python code.
#include
#include
#include
#include
#include
#include
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "password";
WIFI_SSID and WIFI_PASS: Define the SSID and password of the Wi-Fi network that the ESP32 will connect to.
WebServer server(80);
WebServer server(80): Creates an HTTP server instance that listens on port 80 (default HTTP port).
static auto loRes = esp32cam::Resolution::find(320, 240);
static auto midRes = esp32cam::Resolution::find(350, 530);
static auto hiRes = esp32cam::Resolution::find(800, 600);
esp32cam::Resolution::find: Defines three camera resolutions:
loRes: Low resolution (320x240).
midRes: Medium resolution (350x530).
hiRes: High resolution (800x600).
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
esp32cam::capture: Captures a frame from the camera.
Failure Handling: If no frame is captured, it logs a failure and sends a 503 error response.
Logging Success: Prints the resolution and size of the captured image.
Serving the Image:
Sets the content length and MIME type as image/jpeg.
Writes the image data directly to the client.
void handleJpgLo()
{
if (!esp32cam::Camera.changeResolution(loRes)) {
Serial.println("SET-LO-RES FAIL");
}
serveJpg();
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void handleJpgMid()
{
if (!esp32cam::Camera.changeResolution(midRes)) {
Serial.println("SET-MID-RES FAIL");
}
serveJpg();
}
handleJpgLo: Switches the camera to low resolution using esp32cam::Camera.changeResolution(loRes) and calls serveJpg.
handleJpgHi: Switches to high resolution and serves the image.
handleJpgMid: Switches to medium resolution and serves the image.
Error Logging: If the resolution change fails, it logs a failure message to the Serial Monitor.
void setup(){
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam-lo.jpg");
Serial.println(" /cam-hi.jpg");
Serial.println(" /cam-mid.jpg");
server.on("/cam-lo.jpg", handleJpgLo);
server.on("/cam-hi.jpg", handleJpgHi);
server.on("/cam-mid.jpg", handleJpgMid);
server.begin();
}
Serial Initialization:
Initializes the serial port for debugging.
Sets baud rate to 115200.
Camera Configuration:
Sets pins for the AI Thinker ESP32-CAM module.
Configures the default resolution, buffer count, and JPEG quality (80%).
Attempts to initialize the camera and logs the status.
Wi-Fi Setup:
Connects to the specified Wi-Fi network in station mode.
Waits for the connection and logs the device's IP address.
Web Server Routes:
Maps URL endpoints (/cam-lo.jpg, /cam-hi.jpg, /cam-mid.jpg) to their respective handlers.
Server Start:
Starts the web server.
void loop()
{
server.handleClient();
}
server.handleClient(): Continuously listens for incoming HTTP requests and serves responses based on the defined endpoints.
The ESP32-CAM connects to Wi-Fi and starts a web server.
URL endpoints (/cam-lo.jpg, /cam-mid.jpg, /cam-hi.jpg) let the user request images at different resolutions.
The camera captures an image and serves it to the client as a JPEG.
The system continuously handles new client requests.
import cv2
import urllib.request
import numpy as np
cv2: OpenCV library for image processing.
urllib.request: Used to fetch images from the live camera feed via an HTTP request.
numpy: Helps in manipulating and decoding image data into arrays.
url = 'http://192.168.1.101/' # Update the URL if needed
cv2.namedWindow("live transmission", cv2.WINDOW_AUTOSIZE)
url: The IP address of the camera with the endpoint cam-lo.jpg to get the image stream.
cv2.namedWindow: Creates a window to display the live video feed.
while True:
A loop continuously fetches and processes frames from the camera feed until the user quits by pressing 'q'.
img_resp = urllib.request.urlopen(url + 'cam-lo.jpg')
imgnp = np.array(bytearray(img_resp.read()), dtype=np.uint8)
img = cv2.imdecode(imgnp, -1)
urllib.request.urlopen: Sends an HTTP GET request to the camera URL and retrieves an image. Here you can use ‘cam-hi.jpg’ or ‘cam-mid.jpg’ instead. You can use any of the three resolutions of images and see which one gives you the best result.
bytearray: Converts the image data into a binary format for processing.
np.array: Converts the binary data into a NumPy array.
cv2.imdecode: Decodes the NumPy array into an image (OpenCV-readable format).
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(cv2.GaussianBlur(gray, (11, 11), 0), 30, 150, 3)
dilated = cv2.dilate(canny, (1, 1), iterations=2)
cv2.cvtColor: Converts the image to grayscale for easier edge detection.
cv2.GaussianBlur: Applies a Gaussian blur to reduce noise and detail in the image.
Parameters (11, 11) specify the kernel size (area used for the blur).
cv2.Canny: Performs edge detection.
30, 150: Lower and upper thresholds for edge detection.
3: Size of the Sobel kernel.
cv2.dilate: Expands the edges detected by the Canny algorithm to close gaps and make objects more defined.
(1, 1): Kernel size for dilation.
iterations=2: Number of times the dilation is applied.
(Cnt, _) = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.findContours: Finds the outlines of objects in the binary (edge-detected) image.
dilated.copy(): A copy of the dilated image is used to find contours.
cv2.RETR_EXTERNAL: Retrieves only the outermost contours.
cv2.CHAIN_APPROX_NONE: Retains all contour points without compression.
Cnt: List of all detected contours.
cv2.drawContours(img, Cnt, -1, (0, 255, 0), 2)
cv2.drawContours: Draws the detected contours onto the original image.
img: The image to draw on.
Cnt: The list of contours.
-1: Indicates that all contours should be drawn.
(0, 255, 0): The color of the contours (green in BGR format).
2: Thickness of the contour lines.
count_text = f"Objects Counted: {len(Cnt)}"
cv2.putText(img, count_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
f"Objects Counted: {len(Cnt)}": A formatted string showing the number of detected objects.
cv2.putText: Adds the text onto the image.
img: The image to draw on.
(10, 30): Coordinates of the bottom-left corner of the text.
cv2.FONT_HERSHEY_SIMPLEX: The font style.
1: Font scale (size).
(0, 0, 255): Text color (red in BGR format).
2: Thickness of the text.
cv2.imshow("live transmission", img)
cv2.imshow("mit contour", canny)
cv2.imshow: Displays images in separate windows.
"live transmission": Shows the original image with contours and text.
"mit contour": Shows the edge-detected binary image.
key = cv2.waitKey(5)
if key == ord('q'):
break
cv2.waitKey: Waits for 5 milliseconds for a key press.
ord('q'): Checks if the 'q' key is pressed, and if so, breaks the loop to exit the program.
cv2.destroyAllWindows()
cv2.destroyAllWindows: Closes all OpenCV windows when the loop ends.
Fetches the image from the live camera feed.
Processes the image to detect edges and contours.
Counts and draws contours on the image.
Displays the image with the object count overlaid.
Exits when 'q' is pressed.
Power up the ESP32-CAM and connect it to Wi-Fi.
Run the Python script, ensuring the ESP32-CAM URL is correctly set.
See the result of counting the objects in the display.
Note: The background and the objects should be of different colors. If you place black objects on a black background, you will get the wrong results.
Fig: coin counting
Guru Meditation Error: Ensure stable power to the ESP32-CAM.
No Image Display: Check the IP address and ensure the ESP32-CAM is accessible from your computer.
Library Conflicts: Use a virtual environment to isolate Python dependencies.
This project demonstrates a seamless integration of an ESP32-CAM module and Python to build a real-time object-counting system. By using the ESP32-CAM's ability to capture and serve images over Wi-Fi, coupled with Python's powerful OpenCV library, we achieved an efficient and cost-effective solution for object counting and detection.
Throughout the tutorial, we explored each component in detail, from setting up the ESP32-CAM to processing live image streams with Python. Along the way, we learned to customize image resolutions, handle server routes, and enhance detection accuracy using OpenCV functions like edge detection and contour analysis.
This project not only provides a practical application but also serves as a solid foundation for more advanced computer vision systems. Whether you aim to integrate machine learning for object classification or scale this system for industrial monitoring, the possibilities are vast.
We hope this tutorial has inspired you to dive deeper into the world of IoT and computer vision. Happy building!
In today’s tutorial, we’ll show you how to program the ESP32-CAM module. ESP32-CAM module is suitable for building basic surveillance or monitoring systems. Its price is quite reasonable. You can use it for lots of AI-based projects like object detection, face recognition etc.
However, many users face hard luck when setting up and uploading code to ESP32-CAM development boards. This tutorial will provide you with a guideline for successfully programming the ESP32-CAM.
The ESP32-CAM is a standalone development board that integrates an ESP32-S chip, a camera module, onboard flash memory, and a microSD card slot. It features built-in Wi-Fi and Bluetooth connectivity and supports OV2640 or OV7670 cameras with a resolution of up to 2 megapixels.
Ultra-small 802.11b/g/n Wi-Fi + Bluetooth/BLE SoC module
Low-power, dual-core 32-bit processor with a clock speed of up to 240MHz and computing power of 600 DMIPS
520 KB built-in SRAM and 4M external PSRAM
Supports multiple interfaces: UART, SPI, I2C, PWM, ADC, and DAC
Compatible with OV2640 and OV7670 cameras and includes built-in flash storage
Enables Wi-Fi-based image uploads and supports TF cards
Multiple sleep modes for power efficiency
Operates in STA, AP, and STA+AP modes
Dimensions: 27 × 40.5 × 4.5 mm
SPI Flash: Default 32Mbit
RAM: 520KB internal + 4M external PSRAM
Bluetooth: BT 4.2 BR/EDR and BLE
Wi-Fi Standards: 802.11 b/g/n/e/i
Interfaces: UART, SPI, I2C, PWM
TF Card Support: Up to 16GB (4G recommended)
GPIO Pins: 9 available
Image Output Formats: JPEG (only with OV2640), BMP, Grayscale
Antenna: Onboard with 2dBi gain
Security: WPA/WPA2/WPAS-Enterprise/WPS
Power Supply: 5V
Operating Temperature: -20°C to 85°C
Without flash: 180mA @ 5V
With max brightness flash: 310mA @ 5V
Deep sleep mode: 6mA @ 5V
Modem sleep mode: 20mA @ 5V
Light sleep mode: 6.7mA @ 5V
The ESP32-CAM module has fewer accessible GPIO pins compared to a standard ESP32 board since many are allocated for the camera and SD card module. Certain pins should be avoided during programming:
GPIO1, GPIO3, and GPIO0 are essential for uploading code and should not be used for other functions.
GPIO0 is linked to the camera XCLK pin and should remain unconnected during normal operation. It must be pulled to GND only when uploading firmware.
P_OUT Pin: Labeled as VCC on some boards, this pin provides 3.3V or 5V output depending on the solder pad configuration. It cannot be used to power the board—use the dedicated 5V pin instead.
GPIO 2, 4, 12, 13, 14, and 15 are assigned to the SD card reader. If the SD card module is unused, these pins can be repurposed as general I/O.
Notably, the onboard flash LED is connected to GPIO 4, meaning it may turn on when using the SD card reader. To prevent this behaviour, use the following code snippet:
SD_MMC.begin("/sdcard", true);
For an in-depth explanation of the ESP32-CAM pinout and GPIO usage, refer to the Random Nerd Tutorials guide: ESP32-CAM AI-Thinker Pinout Guide: GPIOs Usage Explained.
Following is a full schematic of the ESP32-CAM.
You need to install the CP210X driver on your computer to get the ESP32-CAM working. You can download the driver from here .
No matter which method you choose to program your ESP32-CAM, you need to do the board installation. If ESP32 boards are already installed in your Arduino IDE, feel free to skip this installation section. Go to File > preferences, type https://dl.espressif.com/dl/package_esp32_index.json and click OK.
Go to Tools>Board>Boards Manager and click ‘install’.
Download the ESP32-CAM library from Github (the link is given in the reference section). Follow the path sketch>include library> add.zip library.
Now select the correct path to the library, click on the library folder and press open.
Connect the camera board to your computer. Some camera boards come with a micro USB connector of their own. You can connect the camera to the computer using a micro USB data cable. If the board has no connector, you need to connect the FTDI module to the computer with the data cable. You will need to install the FTDI driver first.
When you’re done with the connection, Go to Tools>boards>esp32>Ai thinker ESP32-CAM
After selecting the board, select the appropriate COM port and upload the following code:
ESP32-CAM programmer shield is made exclusively to program the ESP32-CAM. The shield is equipped with a USB-to-serial converter. The built-in USB-to-serial converter simplifies the process of connecting the board to a computer for programming and debugging. It also includes a microSD card slot for expanded storage, enabling easy data storage and retrieval. Additionally, the shield features a power switch and an LED indicator, allowing for straightforward power control and status monitoring. With its compact design and user-friendly functionality, the ESP32-CAM-MB Programmer Shield is a valuable tool for developers working with the ESP32-CAM-MB board.
Just connect the ESP32-CAM module on top of the Programming Shield as shown below, and connect a USB cable from the Programming Shield to your computer. Now you can program your ESP32-CAM.
First, take a functional USB cable. It should be securely connected and to the USB port of your computer. When plugged in, you should hear a notification sound from your computer. A red LED on the Programming Shield should illuminate. Next, confirm that you have selected the AI Thinker ESP32-CAM board and the appropriate Serial Port. Refer to the image below for guidance.
Press the upload button to upload your code.
Press the IOo button of the programming shield.
The text ‘connecting’ should appear in the output panel.
While holding down the IOo button, press the RST button and release. See the following picture to know the location of the RST button, that you need to press.
When the dots in the text “Connecting …..” stop appearing you can release the IO0 button as well. If the following text appears, it indicates that the code is being uploaded:
When the code is uploaded, you will see the message “Hard resetting via RTS pin…” in the Output Panel. You must press the RST button on the ESP32-CAM module to run the uploaded program. Avoid using the RST button on the Programming Shield. Also, do not press the IO0 button.
This simple Blink program turns on the Flash LED on the ESP32-CAM for 10 milliseconds, then waits for 2 seconds before repeating the cycle.
int flashPin = 4;
void setup() {
pinMode(flashPin, OUTPUT);
}
void loop() {
digitalWrite(flashPin, HIGH);
delay(10);
digitalWrite(flashPin, LOW);
delay(2000);
}
You will see the LED flashing if the code is uploaded without any problem.
Components |
Quantity |
ESP32-CAM WiFi + Bluetooth Camera Module | 1 |
FTDI USB to Serial Converter 3V3-5V | 1 |
Male-to-female jumper wires |
4 |
Female-to-female jumper wire |
1 |
MicroUSB data cable |
1 |
Following is the circuit diagram of this project.
ESP32-CAM WiFi + Bluetooth Camera Module | FTDI USB to Serial Converter 3V3-5V (Voltage selection button should be in 5V position) |
---|---|
5V |
VCC |
GND |
GND |
UOT |
Rx |
UOR |
TX |
IO0 |
GND (FTDI or ESP32-CAM) |
This code functions similarly to a standard Blink program but gradually increases the brightness of the flash LED over 255 steps before turning it off for a second and repeating the cycle.
int flashPin = 4;
void setup() {
pinMode(flashPin, OUTPUT);
}
void loop() {
for (int brightness = 0; brightness < 255; brightness++) {
analogWrite(flashPin, brightness);
delay(1);
}
analogWrite(flashPin, 0);
delay(1000);
}
Since programming the ESP32-CAM (even with the FTDI Programmer) can be cumbersome, it’s advisable to first verify the functionality of the SD card and camera before attempting more complex projects. The following sections outline how to do this.
The ESP32-CAM officially supports up to 4GB microSD cards, but 8GB and 16GB cards generally work fine. Larger cards require reformatting to FAT32, which can be done using guiformat.exe from Ridgecrop.
The test program below creates a file, writes a test message to it, and reads back the content. If the output matches expectations, the SD card is functioning correctly.
#include "SD_MMC.h"
#include "FS.h"
#include "LittleFS.h"
int flashPin = 4;
void setup() {
Serial.begin(115200);
SD_MMC.begin();
LittleFS.begin(true);
// Create and write a test file
File file = LittleFS.open("/test.txt", FILE_WRITE);
file.print("*** Test successful ***");
file.close();
file = LittleFS.open("/test.txt");
while (file.available()) {
Serial.write(file.read());
}
file.close();
// Set the flash LED as output
pinMode(flashPin, OUTPUT);
// turn the LED off
analogWrite(flashPin, 0);
}
void loop() {
}
1. Libraries & Initialization:
#include "SD_MMC.h"
#include "FS.h"
#include "LittleFS.h"
Additionally, we define flashPin to control the flash LED:
int flashPin = 4;
2. Setup Function:
The setup() function initializes serial communication at 115200 baud:
Serial.begin(115200);
Then we need to initialize the SD card and LittleFS file system. The argument true ensures that LittleFS is formatted if it isn't already:
SD_MMC.begin();
LittleFS.begin(true);
A file named test.txt is created, a test message is written, and the file is closed:
File file = LittleFS.open("/test.txt", FILE_WRITE);
file.print("*** Test successful ***");
file.close();
The file is reopened in read mode, its contents are printed to the serial monitor, and then it is closed:
file = LittleFS.open("/test.txt");
while (file.available()) {
Serial.write(file.read());
}
file.close();
We need to turn off the flash LED.
pinMode(flashPin, OUTPUT);
analogWrite(flashPin, 0);
3. Loop Function:
The loop() function remains empty since the entire process occurs within setup().
If the SD card test is successful, you will see *** Test successful ***
in the Serial Monitor.
This confirms that data can be written to and read from the SD card.
For more extensive diagnostics, you can use the SDMMC_Test.ino example provided in the ESP32 library. This program includes additional debugging information and can be accessed via:
File > Examples > Examples for AI-Thinker ESP32-CAM > SDMMC > SDMMC_Test
After verifying the SD card, the next step is to test the camera module. The following is a simplified program that captures an image each time the ESP32-CAM is reset. The image will be saved in the SD card.
#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "SD_MMC.h"
#include "EEPROM.h"
// Pin configuration for AI-Thinker ESP32-CAM module
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
void configCamera() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
esp_camera_init(&config);
}
unsigned int incrementCounter() {
unsigned int counter = 0;
EEPROM.get(0, counter);
EEPROM.put(0, counter + 1);
EEPROM.commit();
return counter;
}
void captureImage() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int counter = incrementCounter();
String filename = "/pic" + String(counter) + ".jpg";
Serial.println(filename);
File file = SD_MMC.open(filename.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
SD_MMC.begin();
EEPROM.begin(16);
configCamera();
captureImage();
esp_deep_sleep_start();
}
void loop() {
}
The configCamera() function sets up the camera with the appropriate pin configurations.
The incrementCounter() function tracks the number of captured images using EEPROM.
The captureImage() function takes a picture and saves it to the SD card.
The setup() function initializes the camera and SD card, captures an image, and puts the ESP32-CAM into deep sleep mode to conserve power.
This basic framework can be expanded for use cases such as motion-triggered or interval-based image capture.
Here are some common problems that you may face while working with the ESP32-CAM. You have provided the solutions too.
Q: Why isn’t my ESP32-CAM connecting to Wi-Fi?
A: Check if you have entered the right SSID and password.
Q: Is there any way to improve the image quality of the camera?
A: Adjust the camera settings in your code, experimenting with different resolutions and frame rates for the best results.
Q: Why are my images blurry or unclear?
A: Poor lighting conditions can degrade image quality. Ensure proper lighting, fine-tune camera settings, and remove the protective lens foil.
Q: Why the camera is not responding to serial monitor commands?
A: Check the connections between the board and the computer. Also, confirm that the baud rate (115200) in your code matches the serial monitor settings.
Q: Why do I see “Timed out waiting for packet header” during code upload?
A: An unstable USB connection may cause this problem. You can try a different USB cable or PORT.
Q: What to do if the ESP32-CAM freezes during code upload?
A: Disconnect and reconnect the USB cable, reset the board, and attempt the upload again. Check that your code isn't causing crashes.
Q: How to resolve the error “A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header”?
A: This may be caused by an incorrect baud rate or a faulty USB cable.
Q: Why isn’t my SD card being detected?
A: The SD card is properly inserted and formatted as FAT32. Cards between 4GB and 16GB work best, while higher-capacity cards may cause issues.
Q: My ESP32-CAM gets hot—should I be concerned?
A: It’s normal for the board to warm up during operation, but excessive heat could indicate a short circuit or power issue.
Q: How can I reduce power consumption?
A: Use sleep modes.
Q: Why isn’t my ESP32-CAM capturing images?
A: Check that the camera module is securely connected, and ensure the correct camera module type is defined in your code.
Q: Can I use ESP32-CAM for video streaming?
A: Use a web server library such as ESP32-CAM-Webserver to stream video over Wi-Fi. Ensure your network can handle the required bandwidth.
Q: Why won’t my ESP32-CAM enter bootloader mode?
A: Ensure GPIO0 is connected to GND, and press the reset button at the correct moment to enter bootloader mode.
Q: Can I upload code wirelessly?
A: Yes, for that you have to use Over-The-Air (OTA) programming.
If you continue to experience issues, double-check the wiring, connections, and settings in your code.
Keywords
ESP32, IoT, Temperature sensor, Humidity sensor, Pressure sensor, Altitude sensor, Arduino IDE, ThingSpeak.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
The evolving generation of wireless technology has made human life a lot easier. Where everything is online and automatic we can easily monitor multiple things virtually from anywhere in the world. The WSN (wireless sensor networks) and Internet of things or IoT play an important role in implementing and accessing these wireless technologies. Smart homes, smart cities and smart weather monitoring systems are examples of such technologies where things are quite simpler or easier.
In this web-server based weather monitoring system, the weather data (from the surrounding environment or of a particular location) like temperature, humidity, pressure etc. is measured with the help of some sensors and then the collected data will be stored on a server after being processed by a microcontroller. Our daily activities are inseparable from weather conditions and various environmental factors. The real-time data collected can be used in research and analysis and the results can be helpful in human life and for improving environmental conditions as well.
The Internet of Things is a system made up of multiple inter-related computing devices. The main factor ‘things’ in IoT is designated to a component that is capable of communicating data over a network (IoT), the thing could be animals, a digital machine, a sensor, a human being etc. Each component of the Internet of Things network is given an individual or a distinct identity and the ability to communicate data or information over a wireless network that is too without the intervention of a human or a computer [8].
An interface medium capable of collecting, controlling, and communicating data among transmitter and recipient electronic equipment or servers is required to build the IoT network[9].
The ESP32 microcontroller series was developed by Espressif Systems. This module (the ESP32) includes a 2.4GHz Wi-Fi chip, memory, a 32-bit Tensilica microcontroller, an antenna, peripheral interfacing abilities, power management modules, and more. This ESP32 module is excellent for the Internet of things because of all of its technological and infrastructural aspects [10].
DHT11 and BMP280 sensors are used to collect the data from their surrounding environment and then communicate the data to the ESP32 module over a particular protocol [11].
The application of this weather monitoring system can also play an important role in the field of agriculture[12 ] to increase productivity, research application, and reducing manpower (by reducing the need to manually monitor the field status). Sometimes in a particular agricultural zone that is hazardous for a human beings, it is quite difficult to manually (offline) monitor the environment or weather conditions. In such cases, this web server based or online weather monitoring system can be of great importance.
Fig. 1
The list and quantity of the components required to implement the web server based weather monitoring system are shown in Table1.
Table: 1
Fig. 2 DHT11 sensor
DHT11 sensor (or temperature and humidity sensor) is a sensor module used to measure humidity and temperature from its surrounding. This sensor module is responsible for monitoring the ambient temperature and humidity of a given area. An NTC (negative temperature co-efficient) temperature sensor and a resistive type humidity sensor make up the sensor. An 8-bit microcontroller is also included. The microcontroller performs ADC (analogue to digital conversion) and outputs a digital signal via the single wire protocol [13].
Some of the technical specifications of the DHT11 sensor are:
Table:2 DHT11 technical specifications
DHT11 sensors can also be used to create a wired sensor system with up to 20 meters of cable.
To measure temperature and humidity, two DHT modules (DHT11 and DHT22) are available on the market. Both modules serve the same purpose, but they have different specifications. The DHT22 sensor, for example, has a wider range of temperature and humidity sensitivity. However, DHT22 is more expensive than DHT11. As a result, you can choose to use any of the modules as per your needs.
Table.3 Interfacing DHT11 with ESP32
Another sensor we are using is the BMP280. The BMP280, also known as the barometric pressure sensor, is a temperature, pressure, and altitude sensor. This sensor's small size and low power consumption make it suitable for mobile applications, GPS modules, and battery-powered devices, among other things[15].
The Bosch BMP280 is based on Bosch's validated ‘Piezo-resistive pressure sensor technology’, which features high accuracy, long-term stability, linearity, and EMC robustness.
The barometric pressure sensor (280) is the successor to the BMP180 sensor, and it is mostly preferred in all areas where precise temperature and pressure measurements are required.
Fig.3 BMP280 Sensor
Fitness, indoor navigation, and GPS refinement are all new technologies which require relative accuracy, and the BMP280 is ideal for them. This module is preferred over the other available modules for temperature records or measurement because of its low TCO (Temperature coefficient of Offset).
The temperature measured with the BMP280 sensor is more accurate than the DHT11 sensor. BMP80 provides a 0.01°C accuracy rate.
Some technical specifications of the BMP280 sensor are:
Table:4 BMP280 Technical specifications
A web server is a place where one can store data online and can access that data at any time and from anywhere in the world [16]. A real-time data is created with the help of a web-server. There are various web services available to store real-time data for research and analysis like AWS (Amazon Web Service), Azure, Firebase etc.
We are using the ThinSpeak web service provided by MathWorks which allows us to send sensor readings/data to the cloud. The ThingSpeak is an open source data platform for the Internet of Things (IoT) applications. We can also visualize and act on or access the data (calculate the data) sent to the ThingSpeak server from ESP32. Two different types of channels are available to store data on the ThingSpeak server namely ‘Public Channel’ and ‘Private Channel’ and one can use either of the available channels to store and display data [17].
ThingSpeak is frequently used for IoT prototyping and proof-of-concept systems that require analytics[18].
We need to give instructions to our ESP32 module so that it can interface, read data from DHT11 and BMP280 sensors and then finally publish the collected data to the ThingSpeak server. Arduino IDE is an integrated development environment used to write, compile and debug the program for the ESP32 module[19].
Fig. Arduino IDE
The process flow of our weather monitoring system is shown below in Chart 1. The process starts with the initialization of ESP32 module which is acting as an interface medium between the sensor modules and the web-server. The ESP32 wi-fi module is continuously searching for the network credentials as per the instruction provided. After connecting to the internet the sensor modules will be initialized and the real-time data [22] collected from the surrounding environment will be pushed to the ThingSpeak web server[23].
Flow Chart: 1
The results observed from the implemented weather monitoring system are shown below. We observed four different environmental conditions which include temperature, humidity, pressure and altitude. The sensor data collected by ESP32 from DHT11 and BMP280 sensors is published to the ThingSpeak web server. On ThingSpeak we have created a channel that contains four fields to store four different environmental factors temperature, humidity, pressure and altitude.
Fig. Hardware
In fig. 4 we can see the ‘Field 1’ which is containing the temperature readings published or communicated from the ESP32 module and saved on the ThingSpeak server.
Similarly, fields 2, 3, and 4 are displaying the humidity, pressure and altitude respectively.
Fig. 4 Temperature (°C)
Fig. 5 Humidity
Fig. 6 Pressure (hPa)
Fig. 7 Altitude
Each factor is monitored at different intervals of time to observe the variations in various environmental factors. From table 5 we can see the various environmental factors and their values at different time intervals for the approximate duration of 24hrs.
Table 5 Variations in weather conditions
We observed the weather conditions (that includes temperature, humidity, pressure and altitude) with our “Web-server based weather monitoring system using ESP32”. The observed real time data is stored on the ThingSpeak server which can be accessed globally. The different values of each (mentioned earlier) environmental factor at different intervals in time are also observed (given in table 5) and the observed result clearly shows the changes in the weather conditions for a full day cycle. Hence, we have successfully implemented and tested the web server based weather monitoring system with ESP32 and ThingSpeak web server.
Internet of Things is a system of multiple inter-related computing devices. The factor ‘thing’ in IoT is designated to an entity capable of communicating data over a network (IOT), which can be a digital machine, sensor, human being, animals etc. Each component that is included in IoT network is assigned with an unique identity called UID and the ability to communicate data over IoT network without any external human or computer intervention.
Hello readers, I hope you all are doing great. In our previous tutorial, we discussed how to upload data to Firebase Real-time Database using ESP32. In this tutorial, we will learn how to read the data stored on the Firebase Database with ESP32.
We can access the data stored in Firebase database from anywhere in the world, which makes this preferable in IoT applications.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
In our previous tutorial, we learnt how to upload an integer value (for demonstration) to Firebase real-time database. So, in this tutorial we will learn how to fetch or receive those integer values from Firebase database.
To access real-time data, we are using two ESP boards where one is used to upload/store the real-time data to the Firebase database and another to read the data stored on the firebase.
Although, it is not required to use two ESP boards, we can also access the previously saved data on the Firebase database with only a single ESP32/ESP8266 board.
We can use the same code for both ESP32 and ESP8266 but we need to make some changes like some of the libraries will be different for ESP8266 and the selection of ESP8266 development board while uploading the code with Arduino IDE.
Fig. 1 Reading data from firebase
Google's Firebase real-time database is a development platform that includes a number of services for managing and authenticating data.
Firebase is a mobile and web app development platform (that also works well with Android APIs) that includes features such as Firebase Cloud, real-time data, and Firebase authentication, among others.
According to Firebase's official documentation (https://firebase.google.com/docs/database), when a user creates a cross-platform application using JavaScript SDKs for Android or Apple, all clients share a single database.
Fig. 1 Firebase Real-time database and ESP32
The following are the main features of the Firebase Real-time database:
The Internet of Things, also known as IoT, is the interconnection of physical objects or devices with sensors and software accessing capabilities in order to communicate data or information over the internet.
We need an interface medium that can fetch, control, and communicate data between sender and receiver electronics devices or servers in order to build an IoT network.
The Firebase real-time database gives you a place to store data from sensors on your level device. With Android APIs, Firebase performs admirably.
Firebase is especially useful for storing data from sensors and syncing it between users in real-time in data-intensive Internet of things (IoT) applications. For the sake of simplicity and clarity, we can say that it is a Google cloud service for real-time collaborative apps.
We are using Arduino IDE to compile and upload code into the ESP32 module. You must have ESP32 board manager installed on your Arduino IDE to program the ESP32 module. To know more about Arduino IDE and how to use it, follow our previous tutorial i.e., on ESP32 programming series. The link is given below:
https://www.theengineeringprojects.com/2021/11/introduction-to-esp32-programming-series.html
Fig. 2 manage libraries
Fig. 3 Install Firebase ESP Client Library
We have already posted a tutorial on our website on getting started with Firebase real-time database and how to post or upload data to Firebase database from ESP32. Where we discussed, how to create a project on Firebase real-time database, authentication, how to access the API key and project URL etc.
So now we do not need to create a new project, we are using the same project and hence same API key and project URL to read or download the data from Firebase real-time database.
Fig. 4 Project Setting
Fig. 5 Project API key
Fig. 6 Project URL
//--add necessary header files
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h" //Provide the token generation process info.
#include "addons/RTDBHelper.h" //Provide the real-time database payload printing info and other helper functions.
// Insert your network credentials
#define WIFI_SSID "ssid"
#define WIFI_PASSWORD "password"
// Insert Firebase project API Key
#define API_KEY "replace this with your project API key"
// ----Insert real-time database URL
#define DATABASE_URL "replace this with your project URL"
//Define Firebase Data object
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
unsigned long sendDataPrevMillis = 0;
int read_data;
bool signupSuccess = false;
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(200);
}
Serial.println();
Serial.print("Connected to... ");
Serial.println(WiFi.localIP());
Serial.println();
// Assigning the project API key
config.api_key = API_KEY;
//Assign the project URL
config.database_url = DATABASE_URL;
/// check signup statue
if (Firebase.signUp(&config, &auth, "", "")) {
Serial.println("ok");
signupSuccess = true;
}
else {
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
// Assign the callback function for token generation task
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
}
void loop()
{
if (Firebase.ready() && signupSuccess && (millis() -
sendDataPrevMillis > 8000 || sendDataPrevMillis == 0))
{
sendDataPrevMillis = millis();
if (Firebase.RTDB.getInt(&fbdo, "/test/int"))
{
if (fbdo.dataType() == "int")
{
read_data = fbdo.intData();
Serial.print("Data received: ");
Serial.println(read_data); //print the data received from the Firebase database
}
}
else
{
Serial.println(fbdo.errorReason()); //print he error (if any)
}
}
}
Fig. 7 Header files
Fig. 8 Helper libraries
Fig. 9 Insert API key
Fig. 10 RTDB URL
Fig. 11 Firebase Data Objects
Fig. 12 Enter Network credentials
Fig. 13 Initialize wifi module
Fig. 14 Fetch/obtain the IP address
Fig. 15 configuring API key
Fig. 16 configuring database URL
Fig. 17 sign up status
Fig. 18
Fig. 19 Fetch data from Firebase RTDB
Fig. 20
Fig. 21 Select development board and COM port
Fig. 22 Data sent Vs Data Received
This concludes the tutorial. I hope you found this of some help and also hope to see you soon with a new tutorial on ESP32.
Hello readers, I hope you all are doing great. In this tutorial, we will learn how to access Firebase (a real-time database) to store and read values or data with ESP32.
It is Google’s mobile application development platform that can be used to can access, monitor and control ESP32 from anywhere in the world with its (firebase) real-time database.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Firebase real-time database is a development platform provided by Google which included multiple services to manage and authenticate data.
Firebase is basically a mobile and web app development platform I as works great with Android APIs) that includes features like firebase cloud, real-time data and Firebase authentication etc.
As per Firebase’s official documentation (https://firebase.google.com/docs/database), whenever a user creates a cross-platform application like with Android, or Apple, JavaScript SDKs, all the clients share a single.
Fig. 1 Firebase Real-time database and ESP32
The main features of the Firebase Real-time database are:
The IoT or Internet of Things is the interconnection of physical objects or devices with sensors and software accessing capabilities to communicate data or information over the internet.
To build an IoT network, we need an interface medium that can fetch, control, and communicate data between sender and receiver electronics devices or servers.
Firebase real-time database provides a platform to store data collected from sensors at the level device. Firebase works great with Android APIs.
Firebase is particularly useful in data-intensive Internet of things (IoT) applications to store from sensors and synch that data between users in real-time. For simplicity or better understanding we can say that it is a cloud service provided by Google for real-time collaborative apps.
The steps involved in creating a Firebase project are:
Fig. 2 Get started
Fig. 3 Create a project
Fig. 4 project name
Fig. 5 Enabling Google Analytics
Fig. 6 Project Created successfully
As per the official firebase documentation at: https://firebase.google.com/docs/auth , the identity of a user is required by most online services or mobile applications or we can say , it handles authentication process and logging in (in this tutorial, the ESP32). Getting to know the identity of a user enables an application to save user data securely in the cloud and provide a consistent personalized service across all of the customer's devices (android phones, computers, applications etc).
Fig. 7 Authentication
Fig. 8 Select authentication method
Next thing is creating a real-time database for the project.
Fig. 9 Real-time database
Fig. 10 Creating database
Fig. 11
Fig. 12 select location
Fig. 13 Accessing project API key
We are using Arduino IDE to compile and upload code into the ESP32 module. You must have the ESP32 board manager installed on your Arduino IDE to program the ESP32 module. To know more about Arduino IDE and how to use it, follow our previous tutorial i.e., on ESP32 programming series. The link is given below:
https://www.theengineeringprojects.com/2021/11/introduction-to-esp32-programming-series.html
Steps to add the necessary libraries in Arduino IDE:
Fig. 14 manage libraries
Fig. 15 Install Firebase ESP Client Library
//--add necessary header files
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h" //Provide the token generation process info.
#include "addons/RTDBHelper.h" //Provide the real-time database payload printing info and other helper functions.
// Insert your network credentials
#define WIFI_SSID "replace this with your netwrok SSID"
#define WIFI_PASSWORD "replace this with your wi-fi password"
// Insert Firebase project API Key
#define API_KEY "replace this with your API key"
// ----Insert real-time database URL
#define DATABASE_URL "replace this with your project URL"
//----Define Firebase Data object
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
int value = 10;
bool signupSuccess = false;
unsigned long sendDataPrevMillis = 0;
void setup()
{
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(100);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP() );
Serial.println();
// Assign the api key ( required)
config.api_key = API_KEY;
// Assign the RTDB URL ( required)
config.database_url = DATABASE_URL;
// Sign up status
if (Firebase.signUp(&config, &auth, "", ""))
{
Serial.println("ok");
signupSuccess = true;
}
else{
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
/* Assign the callback function for the long running token generation task */
config.token_status_callback = tokenStatusCallback; // see addons/TokenHelper.h
Firebase.begin(&config, & auth);
Firebase.reconnectWiFi( true);
}
void loop()
{
if (Firebase.ready() && signupSuccess && (millis() - sendDataPrevMillis >
10000 || sendDataPrevMillis == 0))
{
sendDataPrevMillis = millis();
if (Firebase.RTDB.setInt(&fbdo, "test/int", value))
{
Serial.println("PASSED");
Serial.println("PATH: " + fbdo.dataPath());
Serial.println("TYPE: " + fbdo.dataType());
}
else
{
Serial.println("FAILED");
Serial.println("REASON: " + fbdo.errorReason());
}
value++;
}
}
Before uploading the code in ESP32 board there are some changes you need to make which includes:
Fig. 16 Header files
Fig. 17 Helper libraries
Fig. 18 Insert API key
Fig. 19 RTDB URL
Fig. 20 Firebase Data Objects
Fig. 21 Enter Network credentials
Fig. 22 variable declaration
Fig. 23 Initialize wifi module
Fig. 24 Fetch/obtain the IP address
Fig. 25 configuring API key
Fig. 26 configuring database URL
Fig. 27 sign up status
Fig. 28
Fig. 29 Loop() function
Fig. 30 Select development board and COM port
Fig. 31 Result 1
Fig. 32 Result 2
This concludes the tutorial. I hope you found this of some help and also hope to see you soon with a new tutorial on ESP32.
Hello readers, I hope you all are doing great. In this tutorial, we will learn how to interface the BMP280 sensor with the ES32 module to get temperature, pressure and altitude readings. Later, in this tutorial, we will also discuss how to upload these sensor readings to a web server.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
BMP280 or Barometric pressure sensor is a module used to measure temperature pressure and altitude. The small size and low power consumption feature of this sensor makes it feasible for battery-powered devices, GPS modules and mobile applications etc.
Fig. 1 BMP280 Sensor
The BMP280 is the product of BOSCH which is based on Bosch’s proven Piezo-resistive pressure sensor technology featured with high accuracy, long term stability, linearity and high EMC robustness.
BMP280 is the successor of the BMP180 sensor and offers high performance in all the areas that require precise temperature and pressure measurements.
Emerging applications like fitness, indoor navigation, GPS refinement requires relative accuracy and BMP280 is perfect for such applications. Very low TCO (Temperature coefficient of Offset ) makes this module preferable over other available modules for temperature measurements.
We can also use a DHT11/DHT22 sensor for temperature and humidity measurements but the BMP280 sensor provides better accuracy (i.e., 0.01°C) than DHT sensors.
There are two methods of interfacing BMP280 sensor with ESP32 module:
In the bMP280 Sensor module, there are six interfacing pins including VCC and GND.
Fig. Interfacing BMP280 and ESP32
We are using the I2C protocol for interfacing the two (ESP and BMP280) so only SCL and SDA pins will be used with power pins for interfacing. The SDO and CSB pins will be used only if you are using the SPI protocol for interfacing.
Table 1
We are using Arduino IDE to compile and upload code into the ESP32 module. You must have ESP32 board manager installed on your Arduino IDE to program the ESP32 module. To know more about Arduino IDE and how to use it, follow our previous tutorial i.e., on ESP32 programming series. The link is given below:
https://www.theengineeringprojects.com/2021/11/introduction-to-esp32-programming-series.html
Steps to add the necessary libraries in Arduino IDE:
Fig. 2 manage libraries
Fig. 3 Install library
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(115200);
Serial.println("\nI2C Scanner");
}
void loop()
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
{
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
}
else {
Serial.println("done\n");
}
delay(5000);
}
#include <Wire.h>
#include <Adafruit_BMP280.h>
#define BMP_SDA 21
#define BMP_SCL 22
Adafruit_BMP280 bmp280;
void setup()
{
Serial.begin(115200);
Serial.println("Initializing BMP280");
boolean status = bmp280.begin(0x76);
if (!status)
{
Serial.println("Not connected");
}
}
void loop()
{
float temp = bmp280.readTemperature();
Serial.print("temperature: ");
Serial.print(temp);
Serial.println("*C");
float altitude = bmp280.readAltitude(1011.18);
Serial.print("Altitude: ");
Serial.print(altitude);
Serial.println("m");
float pressure = (bmp280.readPressure()/100);
Serial.print("Pressure: ");
Serial.print(pressure);
Serial.println("hPa");
Serial.println(" ");
delay(1000);
}
Fig. 6
Fig. 7
Fig. 8
Fig. 9
Fig. 10 Select development board and COM port
Fig. 11 Serial monitor output
Most of the industries and organizations these days are shifting to the efficient ways of operating things and the IoT internet of things is one of them.
Internet of Things is a system of multiple inter-related computing devices. The factor ‘thing’ in IoT is designated to an entity capable of communicating data over a network (IOT), which can be a digital machine, sensor, human being, animals etc.
Each component that is included in IoT network is assigned with an unique identity called UID and the ability to communicate data over IoT network without any external human or computer intervention.
Fig. 12 IoT
ThingSpeak is an open data platform for the Internet of Things applications. It is a MathWorks web service that allows users to send sensor readings and data to the cloud. We can also visualize and act on the data (calculate the data) that is sent to ThingSpeak by the devices. The information can be saved in both private and public channels.
ThingSpeak is frequently used for IoT prototyping and proof-of-concept devices that require data analysis.
Downloading and installing the required Library file:
https://github.com/mathworks/thingspeak-arduino
Fig. 13 Adding ThingSpeak library
To check whether the library is successfully added or not:
Fig. 14
Fig, 15 Arduino IDE Library manager
Fig. 16 Getting started for free
Fig. 17 Create new account
Fig. 18 MathWorks Sign in
Fig. 19 New Channel
Fig. 20 Creating channel and respective fields
Fig. 21 save the channel
Fig. 22 Field Chart Edit
// ------style guard ----
#ifdef __cplusplus
extern "C"
{
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
// ------header files----
#include <WiFi.h>
#include "ThingSpeak.h"
#include <Wire.h>
#include <Adafruit_BMP280.h>
#define BMP_SDA 21
#define BMP_SCL 22
Adafruit_BMP280 bmp280;
// -----netwrok credentials
const char* ssid = "public"; // your network SSID (name)
const char* password = "ESP32@123"; // your network password
WiFiClient client;
// -----ThingSpeak channel details
unsigned long myChannelNumber = 4;
const char * myWriteAPIKey = "9R3JZEVBG73YE8BY";
// ----- Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 1000;
void setup()
{
Serial.begin(115200); // Initialize serial
Serial.println("Initializing BMP280");
boolean status = bmp280.begin(0x76);
if (!status)
{
Serial.println("Not connected");
}
//Initialize Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(100);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
// Initialize ThingSpeak
ThingSpeak.begin(client);
}
void loop()
{
if ((millis() - lastTime) > timerDelay )
{
float temp = bmp280.readTemperature(); //temperature measurement
Serial.print("temperature: ");
Serial.print(temp);
Serial.println("*C");
float altitude = bmp280.readAltitude(1011.18); //altitude measurement
Serial.print("Altitude: ");
Serial.print(altitude);
Serial.println("m");
float pressure = (bmp280.readPressure()/100); //pressure measurement
Serial.print("Pressure: ");
Serial.print(pressure);
Serial.println("hPa");
Serial.println(" ");
ThingSpeak.setField(1, temp );
ThingSpeak.setField(2, altitude);
ThingSpeak.setField(3, pressure);
// Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
// pieces of information in a channel. Here, we write to field 1.
int x = ThingSpeak.writeFields(myChannelNumber,
myWriteAPIKey );
if(x == 200)
{
Serial.println("Channel update successful." );
}
else
{
Serial.println("Problem updating channel. HTTP error code " + String(x) );
}
lastTime = millis();
}
}
We are describing only the ThingSpeak server part as the BMP280 and ESP32 interfacing part has already been discussed in the above code description.
Fig. 23 Style guard
Fig. 24 Libraries
Fig. 25
Fig. 26
Fig. 27
Fig. 28
Fig. 29
Fig. 30
Fig. 31
Fig. 32 Sensor readings
Fig. 33 setting respective Fields
Fig. 34
Fig. 35
Fig. 36 ThingSpeak server
Fig. 37 Sensor readings on the Serial monitor
This concludes the tutorial. I hope you found this of some help and also hope to see you soon with a new tutorial on ESP32.