Trong quá trình tìm hiểu và lập trình ESP32, tôi có thấy một bài viết hướng dẫn khá hay, nên tôi dịch lại và giải thích một chút để bạn nào cần có thể tham khảo và trao đổi để làm những project hay hơn. Mã nguồn và nội dung sử dụng trong bài viết được tham khảo từ nguồn: https://RandomNerdTutorials.com/esp32-cam-take-photo-display-web-server/
Bài viết này chúng ta sẽ tìm hiểu cách sử dụng board ESP32 CAM để nhận lệnh “take photo” để chụp hình, sau đó lưu hình ảnh vào bộ nhớ SPIFFS, tiếp theo là hiển thị hình ảnh vừa mới chụp thông qua trình duyệt trên máy tính có kết nối đến board ESP32 này bằng giao thức http.
Trước khi thực hiện nội dung bài hướng dẫn này, các bạn cần thực hiện các bước trong bài trước đây để có thể hiểu cách nạp code cũng như là kết nối phần cứng để lập trình trên arduino ide.
Các thành phần phần cứng cần thết:
+ ESP32-CAM với Camera OV2640
+ Dây nối
+ Module nạp chương trình USBtoTTL PL2303
Các bước thực hiện
1.Cài đặt các thư viện cần thiết cho ESP32
– Cài đặt Arduino IDE cho ESP32
– Cài đặt thư viện ESPAsyncWebServer. Các bạn hãy làm theo hướng dẫn sau để cài đặt thư viện này.
+ Trước tiên, do thư viện này không cài đặt trực tiếp từ Libraries manager nên chúng ta cần tải về từ Github theo link này.
+ Trên Arduino IDE thực hiện Sketch-> include->Library-> Add .ZIP libraries , sau đó chúng ta chọn đến thư viện mà chúng ta vừa download.
– Cài đặt thư viện AsyncTCP, các bạn tải theo link này từ Github và làm tương tự như trên.
2. Cách sử dụng thư viện và các hàm trong Javascript để tạo giao diện điều khiển và hiển thị
– Đầu tiên là chúng ta include đầy đủ các thư viện cần thiết của ESP32, webserver, SPIFFS,..vv
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#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 |
– Tiếp theo là thực hiện khai báo các tham số về mạng mà chúng ta sẽ kết nối
1 2 3 4 5 |
WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Dang ket noi den wifi..."); } |
– Thực hiện tạo object AsyncWebServer sử dụng Port 80
1 |
AsyncWebServer server(80); |
– Khởi tạo biến takeNewPhoto để thực hiện khi chúng ta ra lệnh “chup_hinh ” từ trình duyệt.
1 |
boolean takeNewPhoto = false; |
– Định nghĩa đường dẫn để lưu lại hình ảnh trong SPIFFS
1 |
#define FILE_PHOTO "/photo.jpg" |
– Bước tiếp theo là xây dựng webserver trên ESP32
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE HTML><html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body { text-align:center; } .vert { margin-bottom: 10%; } .hori{ margin-bottom: 0%; } </style> </head> <body> <div id="container"> <h2>ESP32-CAM Last Photo</h2> <p>It might take more than 5 seconds to capture a photo.</p> <p> <button onclick="rotatePhoto();">ROTATE</button> <button onclick="capturePhoto()">CAPTURE PHOTO</button> <button onclick="location.reload();">REFRESH PAGE</button> </p> </div> <div><img src="saved-photo" id="photo" width="70%"></div> </body> <script> var deg = 0; function capturePhoto() { var xhr = new XMLHttpRequest(); xhr.open('GET', "/capture", true); xhr.send(); } function rotatePhoto() { var img = document.getElementById("photo"); deg += 90; if(isOdd(deg/90)){ document.getElementById("container").className = "vert"; } else{ document.getElementById("container").className = "hori"; } img.style.transform = "rotate(" + deg + "deg)"; } function isOdd(n) { return Math.abs(n % 2) == 1; } </script> </html>)rawliteral"; |
Trong phần này các bạn cần có chút kiến thức về html, javascript. Chúng ta chỉ cần quan tâm một số câu lệnh quan trọng để các bạn có thể chỉnh sửa lại theo mục đích của mình.
1 2 3 |
<button onclick="rotatePhoto();">ROTATE</button> <button onclick="capturePhoto()">CAPTURE PHOTO</button> <button onclick="location.reload();">REFRESH PAGE</button> |
Hiểu đơn giản là khi chúng ta mở trình duyệt, gõ địa chỉ IP của ESP32, Webserver sẽ trả lại trình duyệt của chúng ta các nội dung bao gồm mã html, javascript,…Lúc này, html sẽ tạo ra cho chúng ta giao diện mà chúng ta nhìn thấy, đồng thời javascript cung cấp cho chúng ta các hàm để thực hiện. Ví dụ, với các dòng lệnh trên, mỗi lần chúng ra click vào ROTATE, CAPTURE PHOTO, REFRESH PAGE trên trình duyệt webserver sẽ thực hiện các hàm có tên tương ứng rotatePhoto(),capturePhoto(), location.reload()
Các bạn có thể Việt hóa các câu lệnh trên để khi hiển thị trên màn hình trình duyệt là tiếng Việt.
Chúng ta tìm hiểu thêm một chút về các function này.
capturePhoto:
1 2 3 4 5 |
function capturePhoto() { var xhr = new XMLHttpRequest(); xhr.open('GET', "/capture", true); xhr.send(); } |
Với function này, khi chúng ta click vào capture trên trình duyệt, javascipt sẽ thực hiện gửi một GET request với đường dẫn /capture. Lúc này tại ESP32, webserver sẽ nhận bản tin request này và xử lý, nếu thấy trong đường dẫn có /capture, ESP32 sẽ thực hiện chức năng chụp hình. Phần này tôi sẽ nói cụ thể hơn ở dưới.
rotatePhoto:
1 2 3 4 5 6 7 8 |
function rotatePhoto() { var img = document.getElementById("photo"); deg += 90; if(isOdd(deg/90)){ document.getElementById("container").className = "vert"; } else{ document.getElementById("container").className = "hori"; } img.style.transform = "rotate(" + deg + "deg)"; } function isOdd(n) { return Math.abs(n % 2) == 1; } |
Chức năng này được viết bằng javascript để thực hiện quay bức hình của mình trên trình duyệt, tôi ko chuyên về javascript nên ko phân tích chức năng này.
Phần xử lý tại server:
Như đã đề cập ở trên, khi server nhận được request nếu đường dẫn là kí tự “/”, thì sever sẽ phản hồi lại mã html để hiển thị giao diện cho web
1 |
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", index_html);}); |
Khi server nhận được “/CAPTURE”, thì chương trình của chúng ta sẽ set biến takeNewPhoto thành true, sau đó thì trong loop(), chúng ta liên tục kiểm tra biến này, và thực hiện chụp hình khi takeNewPhoto==true
1 |
server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) { takeNewPhoto = true; request->send_P(200, "text/plain", "Taking Photo");}); |
Trong trường hợp server nhận được “/saved-photo, chúng ta sẽ thực hiện gửi hình đã lưu trong bộ nhớ SPIFFS đến trình duyệt đã kết nối.
1 |
server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) { request->send(SPIFFS, FILE_PHOTO, "image/jpg", false);}); |
3. Nạp mã nguồn cho ESP32 thực hiện chụp hình, lưu vào SPIFFS, khởi tạo Webserver.
Toàn bộ mã nguồn các bạn tải tại bài viết gốc của tác giả nhé!
Sử dụng mã nguồn cung cấp bên dưới để biên dịch trên arduino IDE và nạp vào ESP32, tham khảo bài viết trước để biết cách làm.
4. Kết quả
Mã nguồn và nội dung sử dụng trong bài viết được tham khảo từ nguồn: https://RandomNerdTutorials.com/esp32-cam-take-photo-display-web-server/