ESP32-S3入门第九天:摄像头入门与应用

ESP32-S3 入门第九天:摄像头模块使用与图像采集

    • 一、今日目标
    • 二、摄像头模块选型与硬件基础
      • [1. 兼容的摄像头模块](#1. 兼容的摄像头模块)
      • [2. ESP32-S3 摄像头接口特性](#2. ESP32-S3 摄像头接口特性)
    • [三、硬件连接(以 OV2640 为例)](#三、硬件连接(以 OV2640 为例))
      • [1. 引脚定义与接线图](#1. 引脚定义与接线图)
      • [2. 接线注意事项](#2. 接线注意事项)
    • 四、开发环境配置
      • [1. 安装摄像头库](#1. 安装摄像头库)
      • [2. 开发板配置](#2. 开发板配置)
    • 五、基础实验:摄像头初始化与图像采集
      • [1. 测试摄像头是否正常工作](#1. 测试摄像头是否正常工作)
      • [2. 实验现象](#2. 实验现象)
    • [六、进阶实验:Web 服务器实时预览](#六、进阶实验:Web 服务器实时预览)
      • [1. 实验原理](#1. 实验原理)
      • [2. 代码实现](#2. 代码实现)
      • [3. 实验操作与现象](#3. 实验操作与现象)
    • [七、实战项目:图像捕获与 UART 传输](#七、实战项目:图像捕获与 UART 传输)
      • [1. 项目功能](#1. 项目功能)
      • [2. 硬件新增组件](#2. 硬件新增组件)
      • [3. 核心代码片段](#3. 核心代码片段)
      • [4. 上位机接收](#4. 上位机接收)
    • 八、常见问题与解决方案
    • 九、第九天总结与第十天预告
      • [1. 今日成果](#1. 今日成果)
      • [2. 第十天预告:图像识别基础](#2. 第十天预告:图像识别基础)

一、今日目标

  • 了解 ESP32-S3 支持的摄像头模块及硬件特性
  • 掌握摄像头模块的接线方法与驱动配置
  • 实现基础图像采集、本地预览与 WiFi 传输功能
  • 结合往期知识(WiFi/UART)完成图像数据应用

二、摄像头模块选型与硬件基础

1. 兼容的摄像头模块

ESP32-S3 凭借强大的处理能力和专用 DVP 接口,支持多种摄像头模块:

型号 分辨率 接口 优势 适合场景
OV2640 200 万像素(1600×1200) DVP 性价比高、功耗低 基础图像采集
OV5640 500 万像素(2592×1944) DVP 高分辨率 细节拍摄
GC0308 30 万像素(640×480) DVP 体积小、帧率高 实时监控

推荐新手首选: OV2640

驱动成熟、资料丰富、价格低廉(约 20 元),且对 ESP32-S3 兼容性最佳。

2. ESP32-S3 摄像头接口特性

  • 专用 DVP(Digital Video Port)图像接口
  • 支持 8/16 位数据宽度
  • 最高支持 500 万像素摄像头
  • 内置 JPEG 编码器,可直接输出压缩图像(节省带宽)
  • 需占用较多 GPIO(10-12 个),需避免与其他外设冲突

三、硬件连接(以 OV2640 为例)

1. 引脚定义与接线图

OV2640 与 ESP32-S3 的 DVP 接口连接(需严格对应):

OV2640 引脚 功能 ESP32-S3 引脚 备注
VCC 电源 3.3V 禁止接 5V,会烧毁模块
GND 接地 GND 必须与开发板共地
SCL I2C 时钟 GPIO22 用于配置摄像头参数
SDA I2C 数据 GPIO21 用于配置摄像头参数
XCLK 系统时钟 GPIO15 摄像头工作时钟输入
PCLK 像素时钟 GPIO12 像素数据同步时钟
VSYS 场同步 GPIO14 帧同步信号
HREF 行同步 GPIO27 行同步信号
D0-D7 8 位数据 GPIO35-39, 41-42 图像数据总线(共 8 根)
RST 复位 GPIO13 低电平复位(可选接)
PWDN 电源使能 GPIO16 低电平使能(可选接)

2. 接线注意事项

  • 摄像头模块需 3.3V 稳定供电,建议外接电源(若开发板 USB 供电不足)
  • 数据排线尽量短(<10cm),避免信号干扰导致图像花屏
  • 确保 D0-D7 数据线顺序正确,接错会导致图像错乱
  • 先焊接排针再接线,避免引脚接触不良

四、开发环境配置

1. 安装摄像头库

  1. 打开 Arduino IDE,点击「工具」→「管理库」
  2. 搜索「ESP32 Camera」,安装「ESP32 Camera by Espressif Systems」
  3. 安装依赖库「ESP32」(确保版本≥2.0.0)

2. 开发板配置

在「工具」菜单中正确配置:

  • 开发板:ESP32S3 Dev Module
  • Camera Model:OV2640(根据实际模块选择)
  • PSRAM:Enabled(必须启用,用于图像缓存)
  • Flash Size:根据开发板选择(≥4MB)
  • USB Mode:USB-OTG (HID + MSC + CDC)

五、基础实验:摄像头初始化与图像采集

1. 测试摄像头是否正常工作

c 复制代码
#include "esp_camera.h"
#include <WiFi.h>

// WiFi配置(用于后续图像传输)
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";

// OV2640摄像头引脚配置
#define PWDN_GPIO_NUM 16
#define RESET_GPIO_NUM 13
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 21
#define SIOC_GPIO_NUM 22
#define Y9_GPIO_NUM 39
#define Y8_GPIO_NUM 35
#define Y7_GPIO_NUM 34
#define Y6_GPIO_NUM 5
#define Y5_GPIO_NUM 36
#define Y4_GPIO_NUM 37
#define Y3_GPIO_NUM 38
#define Y2_GPIO_NUM 42
#define VSYNC_GPIO_NUM 14
#define HREF_GPIO_NUM 27
#define PCLK_GPIO_NUM 12

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  // 摄像头配置结构体
  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;  // 20MHz时钟
  config.pixel_format = PIXFORMAT_JPEG;  // 输出JPEG格式

  // 根据PSRAM大小配置分辨率
  if(psramFound()){
    config.frame_size = FRAMESIZE_VGA;  // 640x480(带PSRAM可支持)
    config.jpeg_quality = 12;  // 0-63,数字越小质量越高
    config.fb_count = 2;  // 双缓冲(提高帧率)
  } else {
    config.frame_size = FRAMESIZE_QVGA;  // 320x240(无PSRAM)
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // 初始化摄像头
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("摄像头初始化失败:0x%x", err);
    return;
  }

  Serial.println("摄像头初始化成功!");

  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi连接成功");
  Serial.println("IP地址: " + WiFi.localIP().toString());
}

void loop() {
  // 获取一帧图像
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("获取图像失败");
    delay(1000);
    return;
  }

  // 打印图像信息
  Serial.printf("图像尺寸: %zu bytes, 分辨率: %dx%d\n", 
                fb->len, fb->width, fb->height);

  // 释放图像缓冲区
  esp_camera_fb_return(fb);

  delay(2000);  // 每2秒拍摄一次
}

2. 实验现象

  • 若摄像头初始化成功,串口会打印 "摄像头初始化成功!"
  • 成功获取图像后,会显示图像大小和分辨率(如 "图像尺寸: 35648 bytes, 分辨率: 640x480")
  • 若初始化失败,会输出错误代码(可查阅 ESP32 摄像头库文档排查原因)

六、进阶实验:Web 服务器实时预览

1. 实验原理

通过 ESP32-S3 创建 Web 服务器,将摄像头采集的 JPEG 图像以 MJPEG 流的形式发送到浏览器,实现实时预览(帧率约 5-10fps)。

2. 代码实现

c 复制代码
#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>

// WiFi配置
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";

// Web服务器(端口80)
WebServer server(80);

// 摄像头引脚配置(同前一个实验)
#define PWDN_GPIO_NUM 16
#define RESET_GPIO_NUM 13
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 21
#define SIOC_GPIO_NUM 22
#define Y9_GPIO_NUM 39
#define Y8_GPIO_NUM 35
#define Y7_GPIO_NUM 34
#define Y6_GPIO_NUM 5
#define Y5_GPIO_NUM 36
#define Y4_GPIO_NUM 37
#define Y3_GPIO_NUM 38
#define Y2_GPIO_NUM 42
#define VSYNC_GPIO_NUM 14
#define HREF_GPIO_NUM 27
#define PCLK_GPIO_NUM 12

// 生成HTML页面(用于浏览器显示)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <title>ESP32-S3 Camera</title>
  <style>
    body { text-align:center; }
    .cam-container { margin:0 auto; width:640px; }
    img { width:100%; }
  </style>
</head>
<body>
  <h1>ESP32-S3 摄像头实时预览</h1>
  <div class="cam-container">
    <img src="/stream" />  <!-- 显示MJPEG流 -->
  </div>
</body>
</html>
)rawliteral";

// 处理MJPEG流请求
void handleStream() {
  server.sendHeader("Access-Control-Allow-Origin", "*");
  server.sendHeader("Cache-Control", "no-cache");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Connection", "close");
  server.send(200, "multipart/x-mixed-replace; boundary=frame", "");

  while (true) {
    // 获取图像帧
    camera_fb_t * fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("获取图像失败");
      return;
    }

    // 发送MJPEG帧边界
    server.sendContent("--frame\r\n");
    // 发送图像类型和大小
    server.sendHeader("Content-Type", "image/jpeg");
    server.sendHeader("Content-Length", String(fb->len));
    server.sendContent("\r\n");
    
    // 发送图像数据
    server.sendContent((const char*)fb->buf, fb->len);
    server.sendContent("\r\n");
    
    // 释放缓冲区
    esp_camera_fb_return(fb);

    // 检查客户端是否断开连接
    if (!server.client().connected()) {
      break;
    }
  }
}

// 初始化摄像头(同前)
void initCamera() {
  // 摄像头配置代码与基础实验相同
  camera_config_t config;
  // ... 省略配置代码 ...
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("摄像头初始化失败:0x%x", err);
    while(1);
  }
}

void setup() {
  Serial.begin(115200);
  initCamera();

  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi连接成功,IP地址: " + WiFi.localIP().toString());

  // 路由配置
  server.on("/", [](){
    server.send_P(200, "text/html", index_html);
  });
  server.on("/stream", handleStream);

  // 启动服务器
  server.begin();
  Serial.println("Web服务器已启动");
}

void loop() {
  server.handleClient();  // 处理客户端请求
}

3. 实验操作与现象

  1. 上传代码后,在串口监视器获取 ESP32-S3 的 IP 地址(如 192.168.1.100)
  2. 在电脑或手机浏览器中输入该 IP 地址
  3. 浏览器将显示摄像头实时画面(640×480 分辨率)
  4. 移动摄像头,画面会实时更新(延迟约 0.5-1 秒)

七、实战项目:图像捕获与 UART 传输

1. 项目功能

按下按钮时拍摄一张照片,通过 UART2 将 JPEG 图像数据发送到上位机(如电脑),并在 OLED 屏幕显示拍摄状态。

2. 硬件新增组件

  • 轻触按钮(连接 GPIO04,使用 INPUT_PULLUP 模式)
  • 0.96 寸 OLED 屏幕(复用 I2C 接口,GPIO21/SDA, GPIO22/SCL)

3. 核心代码片段

c 复制代码
#include "esp_camera.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED初始化
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

// 按钮引脚
#define BUTTON_PIN 4

void setup() {
  // ... 省略摄像头和WiFi初始化 ...
  
  // 初始化OLED
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);
  oled.setCursor(0, 0);
  oled.println("按按钮拍照");
  oled.display();
  
  // 初始化按钮
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  // 检测按钮按下(低电平)
  if (digitalRead(BUTTON_PIN) == LOW) {
    delay(50);  // 消抖
    if (digitalRead(BUTTON_PIN) == LOW) {
      oled.clearDisplay();
      oled.setCursor(0, 0);
      oled.println("正在拍照...");
      oled.display();

      // 拍摄照片
      camera_fb_t * fb = esp_camera_fb_get();
      if (fb) {
        oled.setCursor(0, 16);
        oled.println("拍照成功!");
        oled.setCursor(0, 32);
        oled.print("大小: ");
        oled.print(fb->len/1024);
        oled.println("KB");
        oled.display();

        // 通过UART2发送图像数据
        Serial2.write(0xFF);  // 帧头
        Serial2.write(0xD8);
        Serial2.write(fb->buf, fb->len);  // 图像数据
        Serial2.write(0xFF);  // 帧尾
        Serial2.write(0xD9);

        esp_camera_fb_return(fb);  // 释放缓冲区
      } else {
        oled.setCursor(0, 16);
        oled.println("拍照失败");
        oled.display();
      }

      delay(2000);  // 防止重复触发
    }
  }
}

4. 上位机接收

使用串口助手(如 SSCOM)接收图像数据:

  1. 配置波特率 115200,选择 UART2 对应的 COM 口
  2. 开启 "十六进制显示",观察是否有 FF D8 开头、FF D9 结尾的 JPEG 数据
  3. 将接收的数据保存为 ".jpg" 文件,即可查看拍摄的照片

八、常见问题与解决方案

问题现象 可能原因 解决方法
摄像头初始化失败(错误代码 0x20004) PSRAM 未启用或故障 1. 在开发板配置中开启 PSRAM 2. 检查 PSRAM 焊接是否良好
图像花屏或条纹 1. 接线错误 2. 时钟频率过高 1. 重新核对 D0-D7 引脚顺序 2. 将 xclk_freq_hz 降低到 16MHz
无法获取图像(返回 NULL) 1. 摄像头未供电 2. 复位引脚配置错误 1. 确认 VCC 接 3.3V 且 GND 连接正确 2. 检查 PWDN 和 RESET 引脚配置
Web 预览卡顿或断开 1. WiFi 信号弱 2. 分辨率过高 1. 靠近路由器或降低分辨率 2. 将 frame_size 改为 FRAMESIZE_QVGA
图像偏色 白平衡未校准 1. 在代码中添加白平衡配置 2. 使用 esp_camera_set_whitebal () 函数

ESP32官方--摄像头应用方案

九、第九天总结与第十天预告

1. 今日成果

  • ✅ 掌握 ESP32-S3 与 OV2640 摄像头的硬件连接方法
  • ✅ 学会配置摄像头参数并实现基础图像采集
  • ✅ 实现 Web 服务器实时预览和 UART 图像传输
  • ✅ 结合按钮、OLED 等外设完成综合应用

2. 第十天预告:图像识别基础

明天将利用 ESP32-S3 的 NPU(神经网络处理单元),实现:

  • 简单的图像识别(如人脸检测、颜色识别)
  • TensorFlow Lite 模型部署
  • 识别结果与硬件联动(如检测到特定物体点亮 LED)
    通过这部分内容,你将初步了解边缘计算在微控制器上的应用,为更复杂的 AIoT 项目打下基础。
相关推荐
一枝小雨4 小时前
FreeRTOS下STM32双缓冲ADC数据采集与处理
stm32·单片机·dma·嵌入式·arm·freertos·adc
乌拉_乌拉_乌拉4 小时前
asrpro2.0天问语音模块搭配STM32(STM32F103c8t6)-杨桃电子开发板
stm32·单片机·嵌入式硬件
『往事』&白驹过隙;5 小时前
ARM环境日志系统的简单设计思路
linux·c语言·数据结构·物联网·iot·日志系统
点灯小铭6 小时前
基于51单片机步数检测计步器无线蓝牙APP上传设计
单片机·嵌入式硬件·毕业设计·51单片机·课程设计
沐欣工作室_lvyiyi10 小时前
基于单片机和LabVIEW的多路数据采集器系统设计(论文+源码)
单片机·嵌入式硬件·毕业设计·数据采集·labview
何须至远11 小时前
机器人市场:犹如一颗深水核弹
stm32·单片机·机器人
m0_5982500014 小时前
串扰12-串扰对信号的影响
笔记·嵌入式硬件·硬件工程
LadyKaka22615 小时前
【IMX6ULL驱动学习】PWM驱动
linux·stm32·单片机·学习
Molesidy16 小时前
【MCU】【STM32】基于STM32CubeMX+CLion的STM32开发环境
stm32·单片机·嵌入式硬件·stm32cubemx·clion