赞
踩
了解如何使用 ESP32-CAM 开发板构建 Web 服务器,该板允许您发送命令以拍摄照片,并在保存在 SPIFFS 中的浏览器中可视化最新拍摄的照片。我们还添加了在必要时旋转图像的选项。
下图显示了我们将在本教程中构建的 Web 服务器。
当您访问 Web 服务器时,您将看到三个按钮:
- 旋转(ROTATE):根据您的 ESP32-CAM 方向,您可能需要旋转照片。
- 拍摄照片(CAPTURE PHOTO):当您单击此按钮时,ESP32-CAM 会拍摄一张新照片并将其保存在 ESP32SPIFFS 中。
请至少等待 5 秒钟,然后刷新网页,以确保 ESP32-CAM 拍摄并存储照片;
- 刷新页面(REFRESH PAGE):单击此按钮时,网页将刷新,并使用最新照片进行更新。
注意:如前所述,拍摄的最新照片存储在 ESP32 SPIFFS 中,因此即使您重新启动开发板,也始终可以访问上次保存的照片。
我们将使用 Arduino IDE 对 ESP32 开发板进行编程。因此,您需要安装 Arduino IDE 以及 ESP32 附加组件:
打开 Arduino>文件>首选项 在附加开发板管理器网址填写以下网址
https://git.oschina.net/dfrobot/FireBeetle-ESP32/raw/master/package_esp32_index.json
然后打开 Arduino >工具>开发板>开发板管理器 搜索ESP32并下载
如果下载失败 参考这个 :
Arduino–Arduino IDE上安装ESP32开发环境(两种方法)
要构建 Web 服务器,我们将使用 ESPAsyncWebServer 库。此库还需要异步 TCP 库才能正常工作。请按照以下步骤安装这些库。
按照以下步骤安装 ESPAsyncWebServer 库:
(下载不成功的话我在下面提供了网盘连接)
或者,下载库后,可以转到 Arduino >项目 >加载库>添加.ZIP 库…,然后选择您刚刚下载的库文件。
ESPAsyncWebServer 库需要 AsyncTCP 库才能工作。请按照以下步骤安装该库:
(下载不成功的话我在下面提供了网盘连接)
或者,下载库后,可以转到 Arduino >项目 >加载库>添加.ZIP 库…,然后选择您刚刚下载的库文件。
将以下代码复制到您的 Arduino IDE。此代码构建一个 Web 服务器,允许您使用 ESP32-CAM 拍摄照片并显示最后拍摄的照片。根据 ESP32-CAM 的方向,您可能需要旋转图片,因此我们还包含了该功能。
Rui Santos Complete project details at https://RandomNerdTutorials.com/esp32-cam-take-photo-display-web-server/ IMPORTANT!!! - Select Board "AI Thinker ESP32-CAM" - GPIO 0 must be connected to GND to upload a sketch - After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. *********/ #include "WiFi.h" #include "esp_camera.h" #include "esp_timer.h" #include "img_converters.h" #include "Arduino.h" #include "soc/soc.h" // Disable brownour problems #include "soc/rtc_cntl_reg.h" // Disable brownour problems #include "driver/rtc_io.h" #include <ESPAsyncWebServer.h> #include <StringArray.h> #include <SPIFFS.h> #include <FS.h> // Replace with your network credentials const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Create AsyncWebServer object on port 80 AsyncWebServer server(80); boolean takeNewPhoto = false; // Photo File Name to save in SPIFFS #define FILE_PHOTO "/photo.jpg" // OV2640 camera module pins (CAMERA_MODEL_AI_THINKER) #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 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"; void setup() { // Serial port for debugging purposes Serial.begin(115200); // Connect to Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); ESP.restart(); } else { delay(500); Serial.println("SPIFFS mounted successfully"); } // Print ESP32 Local IP Address Serial.print("IP Address: http://"); Serial.println(WiFi.localIP()); // Turn-off the 'brownout detector' WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // OV2640 camera module 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; if (psramFound()) { config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; config.fb_count = 1; } // Camera init esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); ESP.restart(); } // Route for root / web page server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", index_html); }); server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) { takeNewPhoto = true; request->send_P(200, "text/plain", "Taking Photo"); }); server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) { request->send(SPIFFS, FILE_PHOTO, "image/jpg", false); }); // Start server server.begin(); } void loop() { if (takeNewPhoto) { capturePhotoSaveSpiffs(); takeNewPhoto = false; } delay(1); } // Check if photo capture was successful bool checkPhoto( fs::FS &fs ) { File f_pic = fs.open( FILE_PHOTO ); unsigned int pic_sz = f_pic.size(); return ( pic_sz > 100 ); } // Capture Photo and Save it to SPIFFS void capturePhotoSaveSpiffs( void ) { camera_fb_t * fb = NULL; // pointer bool ok = 0; // Boolean indicating if the picture has been taken correctly do { // Take a photo with the camera Serial.println("Taking a photo..."); fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); return; } // Photo file name Serial.printf("Picture file name: %s\n", FILE_PHOTO); File file = SPIFFS.open(FILE_PHOTO, FILE_WRITE); // Insert the data in the photo file if (!file) { Serial.println("Failed to open file in writing mode"); } else { file.write(fb->buf, fb->len); // payload (image), payload length Serial.print("The picture has been saved in "); Serial.print(FILE_PHOTO); Serial.print(" - Size: "); Serial.print(file.size()); Serial.println(" bytes"); } // Close the file file.close(); esp_camera_fb_return(fb); // check if file has been correctly saved in SPIFFS ok = checkPhoto(SPIFFS); } while ( !ok ); }
首先,包括与摄像机配合使用、构建 Web 服务器和使用 SPIFFS 所需的库。
#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
#include "driver/rtc_io.h"
#include <ESPAsyncWebServer.h>
#include <StringArray.h>
#include <SPIFFS.h>
#include <FS.h>
接下来,在以下变量中写入您的网络凭证,以便 ESP32-CAM 可以连接到您的本地网络。
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
创建一个异步网络服务器端口 80 上的对象。
AsyncWebServer server(80);
这拍摄新照片布尔变量指示何时需要拍摄新照片。
boolean takeNewPhoto = false;
然后,定义要保存在 SPIFFS 中的照片的路径和名称。
#define FILE_PHOTO "/photo.jpg"
接下来,为 ESP32-CAM AI THINKER 模块定义相机引脚。
#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
接下来,我们有HTML来构建网页:
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";
小伙伴们可以到这里编译前端代码。
我不会详细介绍此 HTML 的工作原理。我们只是快速概述一下。
基本上,创建三个按钮:旋转; 拍摄照片和刷新页面。每张照片调用不同的 JavaScript 函数:旋转照片(),捕获照片()和重新加载().
<button onclick="rotatePhoto();">ROTATE</button>
<button onclick="capturePhoto()">CAPTURE PHOTO</button>
<button onclick="location.reload();">REFRESH PAGE</button>
这 捕获照片() 函数在/捕获指向 ESP32 的 URL,因此它会拍摄一张新照片。
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; }
我们不确定使用JavaScript旋转照片的“最佳”方法是什么。此方法非常有效,但可能有更好的方法可以做到这一点。如果您有任何建议,请与我们分享。
最后,以下部分显示照片。
<div><img src="saved-photo" id="photo" width="70%"></div>
当,您单击 刷新 按钮,它将加载最新的图像。
在 setup(),初始化串行通信:
Serial.begin(115200);
将 ESP32-CAM 连接到本地网络:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
初始化 SPIFFS:
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
ESP.restart();
}
else {
delay(500);
Serial.println("SPIFFS mounted successfully");
}
打印 ESP32-CAM 本地 IP 地址:
Serial.print("IP Address: http://");
Serial.println(WiFi.localIP());
后面的部分,使用正确的设置配置和初始化相机。
接下来,我们需要处理当 ESP32-CAM 在 URL 上收到请求时发生的情况。
当 ESP32-CAM 在根 / URL 上收到请求时,我们会发送 HTML 文本来构建网页。
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/html", index_html);
});
当我们按下”捕获“按钮,我们向 ESP32 /capture URL 发送请求。当这种情况发生时,我们设置拍摄新照片变量到真,以便我们知道是时候拍摄新照片了。
server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) {
takeNewPhoto = true;
request->send_P(200, "text/plain", "Taking Photo");
});
如果 /saved-photo URL 上有请求,请将保存在 SPIFFS 中的照片发送到连接的客户端:
server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(SPIFFS, FILE_PHOTO, "image/jpg", false);
});
最后,启动 Web 服务器。
server.begin();
在 loop(),如果 拍摄新照片 变量为 True,我们调用 capturePhotoSaveSpiffs() 拍摄新照片并将其保存到 SPIFFS。然后,设置 拍摄新照片 变量到 假 .
void loop() {
if (takeNewPhoto) {
capturePhotoSaveSpiffs();
takeNewPhoto = false;
}
delay(1);
}
草图中还有另外两个函数: 检查照片() 和 capturePhotoSaveSpiffs().
这 检查照片() 函数检查照片是否已成功保存到 SPIFFS。
bool checkPhoto( fs::FS &fs ) {
File f_pic = fs.open( FILE_PHOTO );
unsigned int pic_sz = f_pic.size();
return ( pic_sz > 100 );
}
这 capturePhotoSaveSpiffs() 函数拍摄照片并将其保存到 SPIFFS。
void capturePhotoSaveSpiffs( void ) { camera_fb_t * fb = NULL; // pointer bool ok = 0; // Boolean indicating if the picture has been taken correctly do { // Take a photo with the camera Serial.println("Taking a photo..."); fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); return; } // Photo file name Serial.printf("Picture file name: %s\n", FILE_PHOTO); File file = SPIFFS.open(FILE_PHOTO, FILE_WRITE); // Insert the data in the photo file if (!file) { Serial.println("Failed to open file in writing mode"); } else { file.write(fb->buf, fb->len); // payload (image), payload length Serial.print("The picture has been saved in "); Serial.print(FILE_PHOTO); Serial.print(" - Size: "); Serial.print(file.size()); Serial.println(" bytes"); } // Close the file file.close(); esp_camera_fb_return(fb); // check if file has been correctly saved in SPIFFS ok = checkPhoto(SPIFFS); } while ( !ok ); }
实际上应该CH340G的5V接ESP32-CAM的5V,如图只用3.3V供电电压不稳会报错
要上传代码,请按照以下步骤操作:
当程序下载完后打开工具>串口监视器。看到IP地址后
打开浏览器并输入 ESP32-CAM IP 地址。然后,单击“捕获照片”(CAPTURE PHOTO)以拍摄新照片,并等待几秒钟,以便将照片保存在SPIFFS中。
此时在Arduino IDE Serial Monitor窗口中,您应该看到类似的消息:
希望对你有用
我没有撕掉ESP32-CAM 摄像头上的膜,所以照片不是那么清晰。
第一次写文章请多多指教。
库文件下载 提取码:csdn
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。