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. 安装摄像头库
- 打开 Arduino IDE,点击「工具」→「管理库」
- 搜索「ESP32 Camera」,安装「ESP32 Camera by Espressif Systems」
- 安装依赖库「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. 实验操作与现象
- 上传代码后,在串口监视器获取 ESP32-S3 的 IP 地址(如 192.168.1.100)
- 在电脑或手机浏览器中输入该 IP 地址
- 浏览器将显示摄像头实时画面(640×480 分辨率)
- 移动摄像头,画面会实时更新(延迟约 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)接收图像数据:
- 配置波特率 115200,选择 UART2 对应的 COM 口
- 开启 "十六进制显示",观察是否有 FF D8 开头、FF D9 结尾的 JPEG 数据
- 将接收的数据保存为 ".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 () 函数 |
九、第九天总结与第十天预告
1. 今日成果
- ✅ 掌握 ESP32-S3 与 OV2640 摄像头的硬件连接方法
- ✅ 学会配置摄像头参数并实现基础图像采集
- ✅ 实现 Web 服务器实时预览和 UART 图像传输
- ✅ 结合按钮、OLED 等外设完成综合应用
2. 第十天预告:图像识别基础
明天将利用 ESP32-S3 的 NPU(神经网络处理单元),实现:
- 简单的图像识别(如人脸检测、颜色识别)
- TensorFlow Lite 模型部署
- 识别结果与硬件联动(如检测到特定物体点亮 LED)
通过这部分内容,你将初步了解边缘计算在微控制器上的应用,为更复杂的 AIoT 项目打下基础。