734浏览
查看: 734|回复: 0

[ESP8266/ESP32] Firebeetle2 ESP32-S3 新手笔记2-带你搭建网页摄像头

[复制链接]
本帖最后由 伊娃老师 于 2023-10-19 17:48 编辑

引言

本项目最终目标是实现Firebeetle 2 ESP32-S3 的实时摄像头小车制作,上一个分享内容是:
用网页服务器控制板载LED

接着将继续实现搭建一个简单的摄像头服务器。我将分享:
初始化摄像头,配置 Wi-Fi 连接以及设置一个异步 Web 服务器,以便从浏览器中查看摄像头图像。

Firebeetle2 ESP32-S3 新手笔记2-带你搭建网页摄像头图1

所需材料

  • Firebeetle2 ESP32-S3
  • OV2640 摄像头模块
  • 计算机(用于编程)
  • Arduino IDE

准备工作

1.环境准备,请仔细跟着5.1操作

2.必要库安装:AXP313A库

注意事项

Wiki已说明CameraWebServer示例用法,可跟随操作实践。
本次分享案例仅显示摄像头画面。
需要安装ESPAsyncWebServer库,可参考上期分享:用网页服务器控制板载LED

完整代码

复制贴上代码后,修改无线网络ID、密码,
并在串口监视器查看IP,即可使用浏览器查看摄像头画面。
查看的设备需与Firebeetle连接同一个无线网络。

#include <WiFi.h>               // 包含WiFi库
#include <ESPAsyncWebServer.h>  // 包含异步Web服务器库
#include "esp_camera.h"         // 包含ESP相机库
#include "DFRobot_AXP313A.h"    // 包含DFRobot AXP313A电源管理模块库

DFRobot_AXP313A axp;  // 创建一个DFRobot AXP313A对象,用于电源管理

const char *ssid = "eva";           // 你的Wi-Fi网络名称
const char *password = "12345678";  // 你的Wi-Fi密码

// 相机引脚定义
const int XCLK_GPIO_NUM = 45;
const int PWDN_GPIO_NUM = -1;
const int RESET_GPIO_NUM = -1;
const int SIOD_GPIO_NUM = 1;
const int SIOC_GPIO_NUM = 2;
const int Y9_GPIO_NUM = 48;
const int Y8_GPIO_NUM = 46;
const int Y7_GPIO_NUM = 8;
const int Y6_GPIO_NUM = 7;
const int Y5_GPIO_NUM = 4;
const int Y4_GPIO_NUM = 41;
const int Y3_GPIO_NUM = 40;
const int Y2_GPIO_NUM = 39;
const int VSYNC_GPIO_NUM = 6;
const int HREF_GPIO_NUM = 42;
const int PCLK_GPIO_NUM = 5;

AsyncWebServer server(80);  // 创建一个异步Web服务器对象,监听端口80

void configInitCamera() {
  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_VGA;
    config.jpeg_quality = 30;
    config.fb_count = 2;
    config.grab_mode = CAMERA_GRAB_LATEST;
  } else {
    config.frame_size = FRAMESIZE_QVGA;
    config.jpeg_quality = 60;
    config.fb_count = 1;
  }

  esp_err_t err = esp_camera_init(&config);  // 初始化相机
  if (err != ESP_OK) {
    Serial.printf("摄像头初始化失败,错误代码 0x%x", err);
    delay(1000);
    ESP.restart();  // 重启ESP32
  }
}

const char *index_html = R"(
<!DOCTYPE html>
<html>
<head>
  <title>ESP32 摄像头</title>
</head>
<body>
  <img id="cameraImage" width="640" height="480">
  <script>
    function updateCameraImage() {
      var imageElement = document.getElementById("cameraImage");
      imageElement.src = "/image?" + new Date().getTime();
      setTimeout(updateCameraImage, 200); // 0.2秒刷新一次
    }
    updateCameraImage();
  </script>
</body>
</html>
)";

void setup() {
  Serial.begin(115200);  // 启动串口通信
  Serial.println("开始");

  while (axp.begin() != 0) {  // 初始化DFRobot AXP313A电源管理模块
    Serial.println("初始化错误。重试中...");
    delay(1000);
  }
  axp.enableCameraPower(axp.eOV2640);  // 启用摄像头电源

  configInitCamera();  // 初始化摄像头配置

  WiFi.mode(WIFI_STA);         // 将WiFi设置为站点模式
  WiFi.begin(ssid, password);  // 连接WiFi网络
  WiFi.setSleep(false);        //不进入Wi-Fi模块的休眠模式

  while (WiFi.status() != WL_CONNECTED) {  // 等待WiFi连接
    Serial.print(".");
    delay(500);
  }
  Serial.println(WiFi.localIP());  // 打印本地IP地址

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/html", index_html);  // 处理根目录请求并发送HTML页面
  });

  server.on("/image", HTTP_GET, [](AsyncWebServerRequest *request) {
    camera_fb_t *fb = esp_camera_fb_get();
    if (fb) {
      AsyncWebServerResponse *response = request->beginResponse_P(200, "image/jpeg", fb->buf, fb->len);
      request->send(response);   // 发送摄像头图像数据
      esp_camera_fb_return(fb);  // 释放图像缓冲
    }
  });

  server.begin();  // 启动Web服务器
}

void loop() {
  // 这里可以放置与摄像头相关的代码
}

代码解析

引入所需的库

这些库的导入是为了提供所需的功能。
WiFi库用于连接到Wi-Fi网络,
ESPAsyncWebServer库用于创建Web服务器,
esp_camera库用于摄像头控制,
DFRobot_AXP313A库用于电源管理。

#include <WiFi.h>               // 包含WiFi库
#include <ESPAsyncWebServer.h>  // 包含异步Web服务器库
#include "esp_camera.h"         // 包含ESP相机库
#include "DFRobot_AXP313A.h"    // 包含DFRobot AXP313A电源管理模块库

创建变量与定义

在这一部分,创建了一些变量和对象以便在后续的代码中使用。
axp对象用于电源管理
ssid和password是Wi-Fi连接所需的网络名称和密码,
而后面的一系列常量用于定义摄像头引脚的设置。

DFRobot_AXP313A axp;  // 创建一个DFRobot AXP313A对象,用于电源管理

const char *ssid = "eva";           // 你的Wi-Fi网络名称
const char *password = "12345678";  // 你的Wi-Fi密码

// 相机引脚定义
const int XCLK_GPIO_NUM = 45;
const int PWDN_GPIO_NUM = -1;
const int RESET_GPIO_NUM = -1;
const int SIOD_GPIO_NUM = 1;
const int SIOC_GPIO_NUM = 2;
const int Y9_GPIO_NUM = 48;
const int Y8_GPIO_NUM = 46;
const int Y7_GPIO_NUM = 8;
const int Y6_GPIO_NUM = 7;
const int Y5_GPIO_NUM = 4;
const int Y4_GPIO_NUM = 41;
const int Y3_GPIO_NUM = 40;
const int Y2_GPIO_NUM = 39;
const int VSYNC_GPIO_NUM = 6;
const int HREF_GPIO_NUM = 42;
const int PCLK_GPIO_NUM = 5;

AsyncWebServer server(80);  // 创建一个异步Web服务器对象,监听端口80

配置摄像头

这个函数configInitCamera用于配置和初始化摄像头的参数。摄像头的各项参数被设置为config对象中,包括像素格式、分辨率、JPEG质量等。

void configInitCamera() {
  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_VGA;
    config.jpeg_quality = 30;
    config.fb_count = 2;
    config.grab_mode = CAMERA_GRAB_LATEST;
  } else {
    config.frame_size = FRAMESIZE_QVGA;
    config.jpeg_quality = 60;
    config.fb_count = 1;
  }

  esp_err_t err = esp_camera_init(&config);  // 初始化相机
  if (err != ESP_OK) {
    Serial.printf("摄像头初始化失败,错误代码 0x%x", err);
    delay(1000);
    ESP.restart();  // 重启ESP32
  }
}

关于图像质量,可以看这一段代码,
config.jpeg_quality 参数被设置为 30 和 60。这是一个表示 JPEG 图像质量的值,取值范围从 0 到 63,其中 0 表示最低质量,而 63 表示最高质量。较低的质量值会导致更高的压缩,因此图像质量较低,而较高的质量值会导致更低的压缩,从而获得更高的图像质量。
简单说,数字越小,压缩越少,画面质量越高。

而FRAMESIZE可以参考的设置如下:

  • FRAMESIZE_UXGA (1600 x 1200)
  • FRAMESIZE_QVGA (320 x 240)
  • FRAMESIZE_CIF (352 x 288)
  • FRAMESIZE_VGA (640 x 480)
  • FRAMESIZE_SVGA (800 x 600)
  • FRAMESIZE_XGA (1024 x 768)
  • FRAMESIZE_SXGA (1280 x 1024)
if (psramFound()) {
  config.frame_size = FRAMESIZE_VGA;
  config.jpeg_quality = 30; // 这里设置 JPEG 质量
  config.fb_count = 2;
  config.grab_mode = CAMERA_GRAB_LATEST;
} else {
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 60; // 这里设置 JPEG 质量
  config.fb_count = 1;
}

配置HTML页面

这段代码定义了一个包含HTML页面的字符串,该页面包括一个显示摄像头图像的图像元素以及JavaScript代码用于定时刷新图像。

  • <!DOCTYPE html>:这是HTML文档的声明,它指定文档使用的HTML版本。在这里,我们使用HTML5。

  • <html>:HTML文档的根元素,包含了整个HTML页面。

  • <head>:这是头部部分,通常包含了文档的元数据,如标题、字符集设置等。在这里,我们设置了页面的标题为 "ESP32 摄像头"。

  • <body>:这是HTML文档的主体部分,包含了要在页面上显示的内容。

  • <img id="cameraImage" width="640" height="480">:这是一个图像元素,用于显示摄像头图像。id="cameraImage"用于给图像元素一个唯一的标识符,以便在JavaScript中引用它。widthheight` 属性设置了图像元素的宽度和高度。

  • <script>:这是JavaScript代码的起始标签,用于在页面加载时执行JavaScript代码。

  • function updateCameraImage():这是一个JavaScript函数,用于更新摄像头图像。它通过 getElementById 方法获取了 cameraImage 元素,然后设置其 src 属性为 /image,后跟一个时间戳以确保每次都获取新的图像。最后,它使用 setTimeout 函数每 0.2 秒刷新一次图像。

  • updateCameraImage();:这行代码调用 updateCameraImage 函数,以便在页面加载后立即开始刷新摄像头图像。

const char *index_html = R"(
<!DOCTYPE html>
<html>
<head>
  <title>ESP32 摄像头</title>
</head>
<body>
  <img id="cameraImage" width="640" height="480">
  <script>
    function updateCameraImage() {
      var imageElement = document.getElementById("cameraImage");
      imageElement.src = "/image?" + new Date().getTime();
      setTimeout(updateCameraImage, 200); // 0.2秒刷新一次
    }
    updateCameraImage();
  </script>
</body>
</html>
)";

设置setup()函数

在setup()函数中,串口通信被初始化,DFRobot AXP313A电源管理模块被初始化,摄像头配置被设置,Wi-Fi连接被建立,并Web服务器被启动。

void setup() {
  Serial.begin(115200);  // 启动串口通信
  // ...

  while (axp.begin() != 0) {
    // 初始化DFRobot AXP313A电源管理模块
    // ...
  }
  axp.enableCameraPower(axp.eOV2640);  // 启用摄像头电源

  configInitCamera();  // 初始化摄像头配置

  WiFi.mode(WIFI_STA);         // 将WiFi设置为站点模式
  WiFi.begin(ssid, password);  // 连接WiFi网络
  WiFi.setSleep(false);        // 不进入Wi-Fi模块的休眠模式
  // ...

Web服务器路由设置

在这部分中,设置了两个Web服务器路由。第一个路由处理根目录请求,向浏览器发送HTML页面。第二个路由用于获取摄像头图像数据,并将其发送到浏览器。

  • server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {:这是一个Web服务器路由设置,它定义了当浏览器请求根目录("/")时应该执行的操作。HTTP_GET 表示这是一个HTTP GET请求的处理。在这个示例中,注释 // 处理根目录请求并发送HTML页面 提示这是用于处理根目录请求并发送HTML页面的部分代码。实际的HTML页面发送操作被注释掉,因此在此示例中,请求根目录时并未执行具体操作。

  • server.on("/image", HTTP_GET, [](AsyncWebServerRequest *request) {:这是另一个Web服务器路由设置,它定义了当浏览器请求 "/image" 路径时应该执行的操作。同样,HTTP_GET 表示这是一个HTTP GET请求的处理。在这个示例中,当浏览器请求 "/image" 时,会执行以下操作:

    • camera_fb_t *fb = esp_camera_fb_get();:这行代码用于获取摄像头帧缓冲。esp_camera_fb_get() 函数用于从摄像头获取当前帧的图像数据,并将其存储在 fb 变量中。

    • if (fb) {:这是一个条件语句,检查是否成功获取摄像头图像帧。如果成功获取,就会进入下面的操作块。

    • AsyncWebServerResponse *response = request->beginResponse_P(200, "image/jpeg", fb->buf, fb->len);:这行代码创建一个Web服务器响应对象,其中包含了图像数据。响应的HTTP状态码为 200,表示成功。"image/jpeg" 指定了响应的内容类型为JPEG图像。fb->buf 包含了图像数据的指针,而 fb->len 包含了图像数据的长度。

    • request->send(response);:这行代码用于将创建的响应发送给浏览器,从而向浏览器提供了摄像头图像数据。

    • esp_camera_fb_return(fb);:这行代码用于释放摄像头图像缓冲,以便将其用于下一帧。

通过这个Web服务器路由设置,当浏览器请求 "/image" 时,摄像头图像数据将被获取并发送给浏览器,从而允许用户查看实时摄像头图像。这对于创建一个实时视频流非常有用。

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    // 处理根目录请求并发送HTML页面
    // ...
  });

  server.on("/image", HTTP_GET, [](AsyncWebServerRequest *request) {
    camera_fb_t *fb = esp_camera_fb_get();
    if (fb) {
      AsyncWebServerResponse *response = request->beginResponse_P(200, "image/jpeg", fb->buf, fb->len);
      request->send(response);   // 发送摄像头图像数据
      esp_camera_fb_return(fb);  // 释放图像缓冲
    }
  });
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail