# 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("客户端连接已断开");
  }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐