This shows you the differences between two versions of the page.
iothings:proiecte:2022:proiect_costin_vasile [2023/01/19 21:45] costin.vasile1003 |
iothings:proiecte:2022:proiect_costin_vasile [2023/01/19 23:59] (current) costin.vasile1003 |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== WiFi-controlled surveillance system ====== | ====== WiFi-controlled surveillance system ====== | ||
**Student: ** Costin-Emanuel Vasile (ACES) | **Student: ** Costin-Emanuel Vasile (ACES) | ||
+ | |||
+ | All the materials, including the source code and presentation, are [[https://drive.google.com/file/d/151IeY4q_suLt7SAYGVkuXzN0IbmFm5n2/view?usp=sharing|here]] | ||
===== 1. Project overview ===== | ===== 1. Project overview ===== | ||
Line 41: | Line 43: | ||
===== 3. Software ===== | ===== 3. Software ===== | ||
- | TODO | + | The starting point of this project was the official ESP32-CAM webserver example which can be found [[https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer|in this repository]], which I simplified and changed to fit the needs of the project. |
+ | |||
+ | The main functions that the module has to do are the following: | ||
+ | * Connect to WiFi to be accessible from local network | ||
+ | * Host a web platform to provide access to the video stream and movement controls | ||
+ | * Drive the motors according to the commands received from the user through the web platform | ||
+ | |||
+ | The updates I added to the initial code are detailed below. | ||
+ | |||
+ | == Simple HTML index == | ||
+ | From the initial HTML index I removed all the controls available and kept only the video stream and flashlight controlls. On top of this I added the code for the five buttons: | ||
+ | * Added the buttons in the body of the HTML: | ||
+ | <code html> | ||
+ | <button id="drive_stop" style="float:left;" title="">Stop</button> | ||
+ | <button id="drive_front" style="float:left;" title="">Front</button> | ||
+ | <button id="drive_back" style="float:left;" title="">Back</button> | ||
+ | <button id="drive_left" style="float:left;" title="">Left</button> | ||
+ | <button id="drive_right" style="float:left;" title="">Right</button> | ||
+ | </code> | ||
+ | * Added the behavior of the buttons in the <script> side of the HTML index: | ||
+ | |||
+ | <code javascript> | ||
+ | const driveStopButton = document.getElementById('drive_stop') | ||
+ | const driveFrontButton = document.getElementById('drive_front') | ||
+ | const driveBackButton = document.getElementById('drive_back') | ||
+ | const driveLeftButton = document.getElementById('drive_left') | ||
+ | const driveRightButton = document.getElementById('drive_right') | ||
+ | |||
+ | driveStopButton.onclick = () => { | ||
+ | var xhr = new XMLHttpRequest(); | ||
+ | xhr.open('post', 'http://192.168.0.128/?direction=stop', true); | ||
+ | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | ||
+ | xhr.send(); | ||
+ | } | ||
+ | driveFrontButton.onclick = () => { | ||
+ | var xhr = new XMLHttpRequest(); | ||
+ | xhr.open('post', 'http://192.168.0.128/?direction=front', true); | ||
+ | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | ||
+ | xhr.send(); | ||
+ | } | ||
+ | driveBackButton.onclick = () => { | ||
+ | var xhr = new XMLHttpRequest(); | ||
+ | xhr.open('post', 'http://192.168.0.128/?direction=back', true); | ||
+ | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | ||
+ | xhr.send(); | ||
+ | } | ||
+ | driveLeftButton.onclick = () => { | ||
+ | var xhr = new XMLHttpRequest(); | ||
+ | xhr.open('post', 'http://192.168.0.128/?direction=left', true); | ||
+ | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | ||
+ | xhr.send(); | ||
+ | } | ||
+ | driveRightButton.onclick = () => { | ||
+ | var xhr = new XMLHttpRequest(); | ||
+ | xhr.open('post', 'http://192.168.0.128/?direction=right', true); | ||
+ | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | ||
+ | xhr.send(); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == Camera server == | ||
+ | For the server configuration I declared and registered a new URI handler as follows: | ||
+ | <code cpp> | ||
+ | httpd_uri_t direction_index_uri = { | ||
+ | .uri = "/", | ||
+ | .method = HTTP_POST, | ||
+ | .handler = direction_index_handler, | ||
+ | .user_ctx = NULL | ||
+ | }; | ||
+ | |||
+ | httpd_register_uri_handler(camera_httpd, &direction_index_uri); | ||
+ | </code> | ||
+ | |||
+ | The handler registers the following function used to answer to the requests: | ||
+ | <code cpp> | ||
+ | static esp_err_t direction_index_handler(httpd_req_t *req){ | ||
+ | char* buf; | ||
+ | size_t buf_len; | ||
+ | char dir[32] = {0,}; | ||
+ | |||
+ | buf_len = httpd_req_get_url_query_len(req) + 1; | ||
+ | if (buf_len > 1) { | ||
+ | buf = (char*)malloc(buf_len); | ||
+ | if(!buf){ | ||
+ | httpd_resp_send_500(req); | ||
+ | return ESP_FAIL; | ||
+ | } | ||
+ | if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { | ||
+ | if (httpd_query_key_value(buf, "direction", dir, sizeof(dir)) == ESP_OK) { | ||
+ | } else { | ||
+ | free(buf); | ||
+ | httpd_resp_send_404(req); | ||
+ | return ESP_FAIL; | ||
+ | } | ||
+ | } else { | ||
+ | free(buf); | ||
+ | httpd_resp_send_404(req); | ||
+ | return ESP_FAIL; | ||
+ | } | ||
+ | free(buf); | ||
+ | } | ||
+ | |||
+ | Serial.print("New movement request: "); Serial.println(dir); | ||
+ | drive(dir); | ||
+ | return ESP_OK; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | The function above calls the method below which I implemented to drive the motors using the H-bridge module. It sends a PWM signal to the module by calling the built-in Arduino method called ledcWrite. | ||
+ | |||
+ | The pin used to control each side of the H-bridge was mapped to a different PWM output of the ESP32-CAM board. This PWM output represents the first argument of the ledcWrite function, the second being the duty-cycle (where 0 means 0% and 255 means 100% duty-cycle). | ||
+ | |||
+ | <code cpp> | ||
+ | void drive(char* dir){ | ||
+ | ledcWrite(0, 0); | ||
+ | ledcWrite(1, 0); | ||
+ | ledcWrite(2, 0); | ||
+ | ledcWrite(3, 0); | ||
+ | |||
+ | if(strcmp(dir, "right") == 0){ | ||
+ | ledcWrite(0, 150); | ||
+ | ledcWrite(1, 0); | ||
+ | ledcWrite(2, 150); | ||
+ | ledcWrite(3, 0); | ||
+ | } | ||
+ | else if(strcmp(dir, "left") == 0){ | ||
+ | ledcWrite(0, 0); | ||
+ | ledcWrite(1, 150); | ||
+ | ledcWrite(2, 0); | ||
+ | ledcWrite(3, 150); | ||
+ | } | ||
+ | else if(strcmp(dir, "back") == 0) { | ||
+ | ledcWrite(0, 0); | ||
+ | ledcWrite(1, 170); | ||
+ | ledcWrite(2, 170); | ||
+ | ledcWrite(3, 0); | ||
+ | } | ||
+ | else if(strcmp(dir, "front") == 0) { | ||
+ | ledcWrite(0, 170); | ||
+ | ledcWrite(1, 0); | ||
+ | ledcWrite(2, 0); | ||
+ | ledcWrite(3, 170); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
===== 4. Results and demonstration ===== | ===== 4. Results and demonstration ===== | ||
- | TODO | + | I uploaded a demonstrative video [[https://www.youtube.com/shorts/r5RtWcMs1yU|here]]. |
+ | |||
+ | Below are some images with the final prototype of the car. | ||
+ | == Front view == | ||
+ | {{ :iothings:proiecte:2022:front_costin.jpeg?300 |}} | ||
+ | |||
+ | == Side view == | ||
+ | {{ :iothings:proiecte:2022:side_costin.jpeg?300 |}} | ||
+ | |||
+ | == Back view == | ||
+ | {{ :iothings:proiecte:2022:back_costin.jpeg?300 |}} | ||
+ | |||
+ | == Bottom view == | ||
+ | {{ :iothings:proiecte:2022:below_view_costin.jpeg?300 |}} | ||
+ | |||
+ | == Web platform == | ||
+ | {{ :iothings:proiecte:2022:web_platform_costin.png?300 |}} | ||
+ | |||
+ | ===== 5. Resources ===== | ||
+ | [[https://github.com/easytarget/esp32-cam-webserver]] | ||
+ | |||
+ | [[https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer]] | ||
+ | |||
+ | [[https://randomnerdtutorials.com/esp32-cam-car-robot-web-server/]] | ||
+ | |||
+ | [[https://www.optimusdigital.ro/ro/]] |