This shows you the differences between two versions of the page.
iothings:proiecte:2022sric:motion-detection [2023/06/02 03:47] denis.zoican |
iothings:proiecte:2022sric:motion-detection [2023/06/02 08:25] (current) denis.zoican [Software Design] |
||
---|---|---|---|
Line 20: | Line 20: | ||
- [[https://www.emag.ro/fire-cabluri-tata-tata-arduino-modulosy-20-cm-40-buc-5903689130493/pd/DSXWKRMBM/?cmpid=87002&gclid=CjwKCAjwg-GjBhBnEiwAMUvNW5PwIBJRkQa1B_L6_S4qtSAiKIHZwc8SP9CVTK0SFbi0vcymN0HNMhoC2AMQAvD_BwE | Connection wires]] | - [[https://www.emag.ro/fire-cabluri-tata-tata-arduino-modulosy-20-cm-40-buc-5903689130493/pd/DSXWKRMBM/?cmpid=87002&gclid=CjwKCAjwg-GjBhBnEiwAMUvNW5PwIBJRkQa1B_L6_S4qtSAiKIHZwc8SP9CVTK0SFbi0vcymN0HNMhoC2AMQAvD_BwE | Connection wires]] | ||
- | === Hardware Schematics === | + | === Schematics === |
{{:iothings:proiecte:2022sric:motion-detection-schema.png?700|}} | {{:iothings:proiecte:2022sric:motion-detection-schema.png?700|}} | ||
Line 26: | Line 26: | ||
==== Software Design ==== | ==== Software Design ==== | ||
- | The project was developed on 2 different boards: | + | The project was developed on 2 different boards: ESP32 and Raspberry Pi 4. |
- | 1. ESP32 board - the code was written using Arduino IDE, where I used the Arduino libraries | + | |
- | 2. Raspberry Pi 4 - the code was written in Python, where I used OpenCV and Flask | + | === ESP32 board code === |
+ | |||
+ | The first part of the code is the setup part where I define the variables, constants, used libraries and the logic in the setup function. | ||
+ | |||
+ | <code> | ||
+ | #include "WiFi.h" | ||
+ | #include <Arduino.h> | ||
+ | #include <HTTPClient.h> | ||
+ | |||
+ | const int buzzerPin = 12; | ||
+ | const int trigPin = 5; | ||
+ | const int echoPin = 18; | ||
+ | |||
+ | //define sound speed in cm/uS | ||
+ | #define SOUND_SPEED 0.034 | ||
+ | |||
+ | #define WIFI_NETWORK "WIFI_NAME" | ||
+ | #define WIFI_PASSWORD "WIFI_PASS" | ||
+ | #define WIFI_TIMEOUT 20000 | ||
+ | #define API_URL "http://ip-:5000/upload_photo" | ||
+ | |||
+ | long duration; | ||
+ | float distanceCm; | ||
+ | float distanceInch; | ||
+ | |||
+ | |||
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
+ | |||
+ | pinMode(buzzerPin, OUTPUT); | ||
+ | pinMode(trigPin, OUTPUT); | ||
+ | pinMode(echoPin, INPUT); | ||
+ | |||
+ | connectToWifi(); | ||
+ | } | ||
+ | </code> | ||
- | 1. ESP32 board | ||
The code is divided in functions for a better understanding of the logic. | The code is divided in functions for a better understanding of the logic. | ||
+ | |||
The following code demonstrates how the boards connect to WiFi in order to communicate with the Raspberry Pi 4. The board's mode will be set to WIFI_STA, the board being able to connect to an WIFI network. The authentication procedure begins with the use of the network's name and password. If the authentication process does not succeed after 20 seconds (WIFI_TIMEOUT), an error message will be shown. Otherwise, a success message will be shown beside the IP address. | The following code demonstrates how the boards connect to WiFi in order to communicate with the Raspberry Pi 4. The board's mode will be set to WIFI_STA, the board being able to connect to an WIFI network. The authentication procedure begins with the use of the network's name and password. If the authentication process does not succeed after 20 seconds (WIFI_TIMEOUT), an error message will be shown. Otherwise, a success message will be shown beside the IP address. | ||
<code> | <code> | ||
void connectToWifi(){ | void connectToWifi(){ | ||
- | Serial.println("Connecting.. status: " + String(WiFi.status())); | ||
WiFi.mode(WIFI_STA); | WiFi.mode(WIFI_STA); | ||
WiFi.begin(WIFI_NETWORK,WIFI_PASSWORD); | WiFi.begin(WIFI_NETWORK,WIFI_PASSWORD); | ||
Line 55: | Line 89: | ||
</code> | </code> | ||
- | The next function's role is to send a POST request to the raspberry pi board's **upload_photo** endpoint. | + | The next function's role is to send a POST request to the raspberry pi board's **upload_photo** endpoint. The endpoint's url is specified in the API_URL constant. |
<code> | <code> | ||
Line 77: | Line 111: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | The HC-SR04 sensor determines distance by generating sound waves for a set amount of time and then calculating how long it takes for the sound waves to return to the sensor. | ||
+ | In order to get the distance received from the HC-SR04 sensor, the project use the following function. The trigger pin is set to **HIGH** for 10 microseconds, and then we calculate the distance by multiplying the echo pin's value with the sound of speed. | ||
+ | |||
+ | <code> | ||
+ | long getDistance(){ | ||
+ | // Clears the trigPin | ||
+ | digitalWrite(trigPin, LOW); | ||
+ | delayMicroseconds(2); | ||
+ | // Sets the trigPin on HIGH state for 10 micro seconds | ||
+ | digitalWrite(trigPin, HIGH); | ||
+ | delayMicroseconds(10); | ||
+ | digitalWrite(trigPin, LOW); | ||
+ | | ||
+ | // Reads the echoPin, returns the sound wave travel time in microseconds | ||
+ | duration = pulseIn(echoPin, HIGH); | ||
+ | return duration * SOUND_SPEED/2; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | The loop function contains the main logic of the project. We will calculate the distance every 1 second and if the distance is smaller than 50 cm, the buzzer will generate a high pitched sound for 5 seconds and the esp32 board will make a POST request to the raspberry pi 4 board. After this, a photo will be taken and will be saved in the database. | ||
+ | |||
+ | <code> | ||
+ | void loop() { | ||
+ | |||
+ | long distance = getDistance(); | ||
+ | |||
+ | if(distance < 50){ | ||
+ | tone(buzzerPin, 1000); | ||
+ | makeRequest(); | ||
+ | |||
+ | // Wait for 5 seconds | ||
+ | delay(5000); | ||
+ | noTone(buzzerPin); | ||
+ | | ||
+ | } | ||
+ | | ||
+ | delay(1000); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === Raspberry Pi 4 board code === | ||
+ | |||
+ | The code that is used on this board is written in Python. This board will play the role of a server that will store the images in a database. The web server is implemented using the Flask framework and the database is implemented using SQLlite. | ||
+ | In order to create the database, I used the following python code where I defined a table with id, name and the image content. | ||
+ | |||
+ | <code> | ||
+ | import sqlite3 | ||
+ | import os | ||
+ | |||
+ | database_path = "./database.db" | ||
+ | |||
+ | conn = sqlite3.connect(database_path) | ||
+ | cursor = conn.cursor() | ||
+ | |||
+ | cursor.execute(""" | ||
+ | CREATE TABLE IF NOT EXISTS images ( | ||
+ | id INTEGER PRIMARY KEY, | ||
+ | name TEXT, | ||
+ | image_data BLOB | ||
+ | ) | ||
+ | """) | ||
+ | |||
+ | conn.commit() | ||
+ | conn.close() | ||
+ | </code> | ||
+ | |||
+ | In order to capture a photo using the web cam, I used OpenCV, a library of functions for real-time computer vision. I implemented 3 endpoints: | ||
+ | |||
+ | 1. upload_photo | ||
+ | When it receives a POST request, a photo will be taken using the web cam and the image will be saved in the database. | ||
+ | |||
+ | 2. image/{id} | ||
+ | Is a GET endpoint and returns a image based on the received id. | ||
+ | |||
+ | 3. image | ||
+ | Is a GET endpoint and returns the latest image in the database. | ||
+ | |||
+ | <code> | ||
+ | from flask import Flask, send_file | ||
+ | import sqlite3 | ||
+ | import cv2 | ||
+ | |||
+ | |||
+ | app = Flask(__name__) | ||
+ | database_path = "./database.db" # SQLite database file | ||
+ | |||
+ | @app.route('/upload_photo', methods=['POST']) | ||
+ | def upload_photo(): | ||
+ | # Connect to database | ||
+ | conn = sqlite3.connect(database_path) | ||
+ | cursor = conn.cursor() | ||
+ | | ||
+ | # Capture a photo | ||
+ | cam = cv2.VideoCapture(0) | ||
+ | ret, frame = cam.read() | ||
+ | |||
+ | if not ret: | ||
+ | print("failed to grab frame") | ||
+ | return | ||
+ | |||
+ | photo_file_path = "opencv_photo.png" | ||
+ | |||
+ | # Read the photo file as binary | ||
+ | with open(photo_file_path, 'rb') as photo_file: | ||
+ | photo_data = photo_file.read() | ||
+ | |||
+ | # Insert the photo in database | ||
+ | cursor.execute("INSERT INTO images (name, image_data) VALUES (?, ?)", (photo_file_path, photo_data)) | ||
+ | |||
+ | conn.commit() | ||
+ | conn.close() | ||
+ | |||
+ | return "Photo was uploaded" | ||
+ | | ||
+ | @app.route('/image') | ||
+ | def get_image(): | ||
+ | # Connect to database | ||
+ | conn = sqlite3.connect(database_path) | ||
+ | cursor = conn.cursor() | ||
+ | |||
+ | # Retrieve the image from the database wih the highest ID | ||
+ | cursor.execute("SELECT image_data FROM images WHERE id= (SELECT MAX(id) from images)") | ||
+ | result = cursor.fetchone() | ||
+ | |||
+ | # Save the image data to a temporary file | ||
+ | image_data = result[0] | ||
+ | temp_file = "tempFile.jpg" # Path to temporary file | ||
+ | with open(temp_file, "wb") as file: | ||
+ | file.write(image_data) | ||
+ | |||
+ | # Close the database connection | ||
+ | conn.close() | ||
+ | |||
+ | # Send the image file | ||
+ | return send_file(temp_file, mimetype='image/jpeg') | ||
+ | | ||
+ | @app.route('/image/<image_id>') | ||
+ | def get_image_id(image_id): | ||
+ | # Connect to the database | ||
+ | conn = sqlite3.connect(database_path) | ||
+ | cursor = conn.cursor() | ||
+ | |||
+ | # Retrieve the image from the database based on the id | ||
+ | cursor.execute("SELECT image_data FROM images WHERE id=?", (image_id,)) | ||
+ | result = cursor.fetchone() | ||
+ | |||
+ | # Save the image data to a temporary file | ||
+ | image_data = result[0] | ||
+ | temp_file = "tempFile.jpg" | ||
+ | with open(temp_file, "wb") as file: | ||
+ | file.write(image_data) | ||
+ | |||
+ | # Close the database connection | ||
+ | conn.close() | ||
+ | |||
+ | # Send the image file | ||
+ | return send_file(temp_file, mimetype='image/jpeg') | ||
+ | |||
+ | if __name__ == '__main__': | ||
+ | app.run(host='0.0.0.0', port=5000) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | The last photo can be viewed by accessing the url: raspberry-pi-ip:5000/image | ||
+ | === Demo === | ||
+ | |||
+ | YouTube link: https://www.youtube.com/watch?v=mqfNEk4cIAE | ||
+ | |||
+ | === References === | ||
+ | |||
+ | - [[https://docs.opencv.org/4.x/|OpenCV]] | ||
+ | - [[https://flask.palletsprojects.com/en/2.3.x/|Flask]] | ||
+ | - [[https://projecthub.arduino.cc/Isaac100/getting-started-with-the-hc-sr04-ultrasonic-sensor-7cabe1|HC-SR04]] | ||
+ | - [[https://www.sqlite.org/docs.html|SQLlite]] | ||