esp32-can实现简单的网页显示摄像头内容
# ESP32-CAM 网页摄像头 - 最简实现
## 功能概述
本项目实现了一个基于 ESP32-CAM 开发板的网页摄像头系统,通过 WiFi 连接,在网页浏览器中实时查看摄像头画面。
## 硬件要求
| 硬件 | 规格 |
|------|------|
| 开发板 | ESP32-CAM (AI-Thinker 板型) |
| 摄像头模块 | OV2640 (板载) |
| 烧录工具 | USB-TTL 串口模块 |
## 接线说明
### USB-TTL 串口模块连接
| ESP32-CAM | USB-TTL |
|-----------|---------|
| VCC | 5V |
| GND | GND |
| TX | RX |
| RX | TX |
### 烧录模式
烧录程序时,需要将 **GPIO0** 连接到 **GND**,然后给开发板上电,进入烧录模式。
烧录完成后,断开 GPIO0 和 GND 的连接,按复位按钮启动程序。
## 软件要求
- **Arduino IDE** (版本 1.8.19 或更高)
- **ESP32 开发板支持包** (版本 2.0.0 或更高)
## Arduino IDE 配置
### 1. 安装 ESP32 开发板支持包
在 Arduino IDE 中,打开 `File > Preferences`,在 `Additional Boards Manager URLs` 中添加:
```
https://dl.espressif.com/dl/package_esp32_index.json
```
然后打开 `Tools > Board > Boards Manager`,搜索 `esp32`,安装 `esp32 by Espressif Systems`。
### 2. 选择开发板
```
Tools > Board > ESP32 Arduino > AI Thinker ESP32-CAM
```
### 3. 配置开发板参数
| 参数 | 设置 |
|------|------|
| Upload Speed | 115200 |
| Flash Frequency | 40MHz |
| Flash Mode | QIO |
| Flash Size | 4MB (32Mb) |
| PSRAM | Enabled |
| Partition Scheme | Huge APP (3MB No OTA/1MB SPIFFS) |
## 代码结构
```
sketch_jul6a.ino
├── 头文件引入
│ ├── esp_camera.h // 摄像头驱动库
│ └── WiFi.h // WiFi 库
├── WiFi 配置
│ ├── ssid // WiFi 网络名称
│ └── password // WiFi 密码
├── 摄像头引脚配置
│ └── AI-Thinker ESP32-CAM 引脚定义
├── setup() 函数
│ ├── 串口初始化
│ ├── 摄像头配置与初始化
│ ├── WiFi 连接
│ └── 启动 HTTP 服务器
├── loop() 函数
│ └── 空循环(服务器在 startCameraServer 中运行)
└── startCameraServer() 函数
├── 创建 HTTP 服务器
├── 处理客户端连接
└── 发送摄像头视频流
```
## 工作原理
### 摄像头初始化
使用 `esp_camera_init()` 函数初始化摄像头,配置包括:
- 引脚映射:将 ESP32 GPIO 引脚连接到摄像头模块
- 图像格式:JPEG 压缩格式,节省带宽
- 帧大小:QVGA (320x240),平衡画质和性能
- 双缓冲:提高帧率
### HTTP 视频流
使用 `multipart/x-mixed-replace` 协议实现实时视频流:
1. 客户端浏览器访问 ESP32 的 IP 地址
2. ESP32 发送 HTTP 响应头,声明内容类型为 `multipart/x-mixed-replace`
3. ESP32 持续捕获摄像头画面,以 JPEG 格式发送
4. 每个帧以 `--frame` 边界标记分隔
5. 浏览器自动更新显示新的图片帧,形成视频效果
## 使用步骤
1. 修改代码中的 WiFi 配置:
```cpp
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
```
2. 将 GPIO0 连接到 GND,进入烧录模式
3. 点击 Arduino IDE 的上传按钮
4. 上传完成后,断开 GPIO0 和 GND 的连接
5. 按复位按钮启动程序
6. 打开串口监视器(波特率 115200),查看 ESP32 获取的 IP 地址
7. 在浏览器中访问该 IP 地址,查看摄像头画面
## 常见问题
### Q1: 摄像头初始化失败
**可能原因:**
1. PSRAM 未启用 - 在 Tools > PSRAM 中选择 Enabled
2. 分区方案不正确 - 使用 Huge APP 分区方案
3. 摄像头模块损坏
### Q2: WiFi 连接失败
**可能原因:**
1. WiFi SSID 或密码错误,不能有空格
2. ESP32 与路由器距离过远
3. WiFi 频段不支持(ESP32-CAM 只支持 2.4GHz)
### Q3: 浏览器无法显示画面
**可能原因:**
1. 网络不通 - 确保电脑和 ESP32 在同一网络
2. 端口被占用 - 默认使用 80 端口
3. 防火墙阻止了连接
## 技术参数
| 参数 | 值 |
|------|------|
| 帧大小 | 320x240 (QVGA) |
| 帧率 | 约 10 FPS |
| JPEG 质量 | 12 (0-63,越小越好) |
| 视频格式 | MJPEG (multipart/x-mixed-replace) |
| HTTP 端口 | 80 |
## 注意事项
1. ESP32-CAM 板载天线信号较弱,建议使用外置天线
2. 长时间运行可能导致发热,注意散热
3. 建议使用 5V/2A 以上的电源适配器
4. 视频流会占用较多带宽,建议在局域网内使用
/*
* ESP32-CAM 网页摄像头 - 最简实现
*
* 功能:通过 WiFi 连接,在网页浏览器中实时查看摄像头画面
* 硬件:ESP32-CAM (AI-Thinker 板型)
* 开发环境:Arduino IDE + ESP32 开发板支持包
*
* 作者:Auto Generated
* 日期:2026-07-06
*/
// 引入摄像头驱动库 - 用于操作 ESP32 内置摄像头接口
#include "esp_camera.h"
// 引入 WiFi 库 - 用于连接无线网络
#include <WiFi.h>
/* ==================== WiFi 配置 ==================== */
// WiFi 网络名称 (SSID) - 修改为你的 WiFi 名称
const char* ssid = "xiaomi11";
// WiFi 密码 - 修改为你的 WiFi 密码
const char* password = "110120119";
/* ==================== 摄像头引脚配置 (AI-Thinker ESP32-CAM) ==================== */
// PWDN 引脚 - 摄像头电源控制,低电平有效
#define PWDN_GPIO_NUM 32
// RESET 引脚 - 摄像头复位,-1 表示不使用硬件复位
#define RESET_GPIO_NUM -1
// XCLK 引脚 - 摄像头时钟信号
#define XCLK_GPIO_NUM 0
// SIOD 引脚 - SCCB/I2C 数据线
#define SIOD_GPIO_NUM 26
// SIOC 引脚 - SCCB/I2C 时钟线
#define SIOC_GPIO_NUM 27
// Y9-Y2 引脚 - 摄像头数据总线 (8位)
#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
// VSYNC 引脚 - 垂直同步信号 (帧同步)
#define VSYNC_GPIO_NUM 25
// HREF 引脚 - 水平参考信号 (行同步)
#define HREF_GPIO_NUM 23
// PCLK 引脚 - 像素时钟信号
#define PCLK_GPIO_NUM 22
// 声明摄像头服务器函数 - 在 setup() 中调用
void startCameraServer();
/* ==================== 初始化函数 ==================== */
void setup() {
// 初始化串口通信,波特率 115200 - 用于调试输出
Serial.begin(115200);
// 启用调试输出到串口
Serial.setDebugOutput(true);
Serial.println();
/* 摄像头配置结构体 */
camera_config_t config;
// LEDC 通道 - 用于 PWM 控制
config.ledc_channel = LEDC_CHANNEL_0;
// LEDC 定时器 - 用于生成 PWM 信号
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; // I2C 数据
config.pin_sscb_scl = SIOC_GPIO_NUM; // I2C 时钟
config.pin_pwdn = PWDN_GPIO_NUM; // 电源控制
config.pin_reset = RESET_GPIO_NUM; // 复位引脚
/* 摄像头参数配置 */
config.xclk_freq_hz = 20000000; // 时钟频率 20MHz
config.pixel_format = PIXFORMAT_JPEG; // 图像格式:JPEG (压缩格式,节省带宽)
/* 图像质量配置 */
config.frame_size = FRAMESIZE_QVGA; // 帧大小:320x240 (QVGA)
config.jpeg_quality = 12; // JPEG 质量:0-63,越小质量越好
config.fb_count = 2; // 帧缓冲数量:2 (双缓冲,提高帧率)
/* 初始化摄像头 */
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
// 初始化失败,输出错误码
Serial.printf("摄像头初始化失败,错误码: 0x%x", err);
return;
}
/* 连接 WiFi */
Serial.print("正在连接 WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// 等待 WiFi 连接成功
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// WiFi 连接成功,输出信息
Serial.println("");
Serial.println("WiFi 连接成功");
Serial.print("IP 地址: ");
Serial.println(WiFi.localIP());
Serial.println("");
Serial.println("========================================");
Serial.print("摄像头流已就绪!访问: http://");
Serial.println(WiFi.localIP());
Serial.println("========================================");
// 启动摄像头 HTTP 服务器
startCameraServer();
}
/* ==================== 主循环函数 ==================== */
void loop() {
// 由于 startCameraServer() 内部是无限循环,
// 这里不会执行到。保留空循环是 Arduino 规范。
delay(10);
}
/* ==================== 摄像头 HTTP 服务器 ==================== */
void startCameraServer() {
// 创建 WiFi 服务器,监听 80 端口 (HTTP 默认端口)
WiFiServer server(80);
// 启动服务器
server.begin();
Serial.println("HTTP 服务器已启动,监听端口 80");
// 无限循环处理客户端连接
while (true) {
// 检查是否有新的客户端连接
WiFiClient client = server.available();
if (!client) {
// 没有客户端,短暂延时后继续检查
delay(10);
continue;
}
Serial.println("新客户端连接");
/* 等待客户端发送 HTTP 请求 */
unsigned long startTime = millis(); // 记录开始时间
while(!client.available()){
// 超时检测:超过 1 秒无数据则断开连接
if(millis() - startTime > 1000){
Serial.println("客户端无响应,超时断开");
client.stop();
// 回到循环开始,等待下一个客户端
continue;
}
delay(10);
}
// 读取客户端发送的 HTTP 请求(读取到回车符为止)
String req = client.readStringUntil('\r');
// 清空输入缓冲区
client.flush();
Serial.print("收到请求: ");
Serial.println(req);
/* 发送 HTTP 响应头 */
// HTTP 状态码 200 OK
client.println("HTTP/1.1 200 OK");
// 内容类型:multipart/x-mixed-replace (多部分混合替换)
// 这是一种实时流协议,浏览器会持续接收新的图片帧
client.println("Content-Type: multipart/x-mixed-replace; boundary=frame");
// 响应头结束标记(空行)
client.println();
/* 持续发送摄像头画面 */
while (client.connected()) {
// 获取摄像头帧缓冲区
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
// 获取帧失败,输出错误信息
Serial.println("摄像头捕获失败");
continue;
}
/* 发送 JPEG 帧 */
// 帧边界标记(与响应头中的 boundary 对应)
client.print("--frame\r\n");
// 帧内容类型:JPEG 图片
client.print("Content-Type: image/jpeg\r\n");
// 帧内容长度
client.print("Content-Length: ");
client.println(fb->len);
// 帧头结束标记
client.println();
// 发送 JPEG 数据
client.write(fb->buf, fb->len);
// 帧结束标记
client.println();
// 释放帧缓冲区(非常重要!否则会内存泄漏)
esp_camera_fb_return(fb);
// 延时 100ms,控制帧率约 10 FPS
delay(100);
}
// 客户端断开连接,停止客户端
client.stop();
Serial.println("客户端连接已断开");
}
}
更多推荐


所有评论(0)