项目: esp32-cam | ESP-IDF: v5.5.4 | 芯片: ESP32 (Xtensa LX6 @240MHz) 固件大小: 946 KB | 编译步骤: 1063/1063 通过

目录

  1. 整体架构概览
  2. 硬件层
  3. ESP-IDF 系统层
  4. esp32-camera 驱动层
  5. 应用层
  6. 数据流详解
  7. HTTP 接口说明
  8. 引脚配置
  9. 存储器布局
  10. 构建与烧录

1. 整体架构概览

┌──────────────────────────────────────────────────────────────┐
│                        客户端 (浏览器)                        │
│              http://<ESP32-IP>/      /capture      /stream    │
└──────────────────────────┬───────────────────────────────────┘
                           │ HTTP over WiFi
┌──────────────────────────▼───────────────────────────────────┐
│                      应用层 (Application)                     │
│  app_main() → wifi_event_handler → HTTP Server               │
│  ├── GET /         首页 HTML (内嵌流预览)                     │
│  ├── GET /capture  单帧 JPEG 拍照                             │
│  └── GET /stream   MJPEG 视频流 (multipart/x-mixed-replace)   │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    esp32-camera 驱动层                        │
│  Component Registry: espressif/esp32-camera                   │
│  ├── esp_camera.c       核心驱动 (cam_hal / sccb-ng / xclk)  │
│  ├── ov2640.c           传感器驱动 (OV3640 / OV5640 / ...)   │
│  ├── to_jpg / to_bmp    图像格式转换                          │
│  └── ll_cam.c           ESP32 底层 DVP 寄存器操作             │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                     ESP-IDF 系统层 (v5.5.4)                   │
│  FreeRTOS │ NVS Flash │ LWIP TCP/IP │ esp_wifi │ esp_http_server │
│  esp_psram │ esp_event │ esp_netif │ esp_timer │ spi_flash   │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                       硬件层 (ESP32-CAM)                      │
│  ESP32 SoC │ OV2640 Sensor │ 4MB PSRAM │ 4MB SPI Flash │ LED │
└──────────────────────────────────────────────────────────────┘

2. 硬件层

2.1 ESP32-CAM 开发板

典型的 AI-Thinker ESP32-CAM 开发板硬件资源:

组件 型号/规格 用途
主控 ESP32 (Xtensa LX6 @240MHz) 双核处理器,集成 WiFi / BT
摄像头 OV2640 (200万像素) JPEG/YCbCr/RGB 输出,最高 UXGA
PSRAM 4 MB SPI Pseudo-SRAM 帧缓冲区 (framebuffer)
Flash 4 MB SPI NOR Flash 固件存储 + 分区表
LED GPIO4 控制 闪光灯 (可程序控制)

2.2 OV2640 传感器

OV2640 是经典的 1/4 英寸 CMOS 图像传感器:

  • 分辨率: UXGA (1600×1200) 向下兼容 SVGA/XGA
  • 输出格式: JPEG、YCbCr422、RGB565
  • 控制接口: SCCB (兼容 I2C),通过 SIOD/SIOC 引脚
  • 数据接口: DVP 8 位并行总线 (D0-D7) + PCLK/HREF/VSYNC 同步信号
  • 时钟: XCLK 20 MHz (由 ESP32 LEDC 提供)

3. ESP-IDF 系统层

3.1 组件依赖图

esp32-cam (main)
  │
  ├── espressif/esp32-camera (Component Registry) ── 外部组件
  │     └── espressif/esp_jpeg                         外部组件
  │
  ├── esp_http_server ── HTTP 服务框架
  │     ├── esp_event ── 事件循环
  │     └── http_parser ── HTTP 协议解析
  │
  ├── esp_wifi ── WiFi 驱动
  │     ├── esp_netif ── 网络抽象
  │     └── esp_phy ── 射频物理层
  │
  ├── lwip ── TCP/IP 协议栈
  ├── nvs_flash ── 非易失存储
  ├── esp_psram ── PSRAM 支持
  ├── esp_timer ── 高精度定时器
  │
  ├── FreeRTOS ── 实时操作系统
  │     ├── soc ── SoC 抽象
  │     ├── hal ── 硬件抽象层
  │     └── esp_hw_support ── 硬件支持函数
  │
  └── 公共基础 (自动链接)
        ├── log ── 日志系统
        ├── heap ── 内存管理
        ├── newlib ── C 标准库
        └── spi_flash ── Flash 操作

3.2 关键配置 (sdkconfig.defaults)

# PSRAM 支持
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_CAPS_ALLOC=y

# WiFi 缓冲区
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32

# 主任务栈 4KB
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096

# 单应用分区 (简化分区表)
CONFIG_PARTITION_TABLE_SINGLE_APP=y

4. esp32-camera 驱动层

4.1 组件结构

espressif/esp32-camera 通过 ESP-IDF Component Registry 引入,自动下载到 managed_components/

managed_components/espressif__esp32-camera/
├── driver/
│   ├── esp_camera.c         # 核心 API: esp_camera_init() / esp_camera_fb_get()
│   ├── cam_hal.c            # 硬件抽象层 (HAL),封装 DVP / CSI 操作
│   ├── sccb-ng.c            # SCCB (I2C) 控制器驱动
│   └── esp_camera_af.c      # 自动对焦 (仅 OV5640 AF)
├── sensors/
│   ├── ov2640.c             # OV2640 传感器驱动 ← 本项目使用
│   ├── ov3660.c             # OV3660 传感器
│   ├── ov5640.c             # OV5640 传感器 (含 AF)
│   ├── ov7670.c             # OV7670 传感器
│   ├── gc032a.c             # 格科微 GC032A
│   └── ...                  # 其他传感器
├── conversions/
│   ├── to_jpg.cpp           # RGB/YUV → JPEG 转换
│   ├── to_bmp.c             # RGB → BMP 转换
│   └── yuv.c                # YUV 色彩空间转换
└── target/
    └── esp32/
        └── ll_cam.c         # ESP32 底层 DVP 寄存器操作

4.2 核心 API

// 初始化摄像头
esp_err_t esp_camera_init(const camera_config_t *config);

// 获取一个帧缓冲区 (从 DMA 队列中取出)
camera_fb_t *esp_camera_fb_get(void);

// 归还帧缓冲区 (放回 DMA 队列)
void esp_camera_fb_return(camera_fb_t *fb);

// 格式转换 (非 JPEG 传感器需要)
bool frame2jpg(camera_fb_t *fb, int quality, uint8_t **out, size_t *out_len);

4.3 摄像头配置结构体

camera_config_t config = {
    // -- 引脚定义 (DVP 并行接口) --
    .pin_pwdn  = 32,    .pin_reset = -1,
    .pin_xclk  = 0,     .pin_sccb_sda = 26,  .pin_sccb_scl = 27,
    .pin_d0 = 5,  .pin_d1 = 18,  .pin_d2 = 19,  .pin_d3 = 21,
    .pin_d4 = 36, .pin_d5 = 39,  .pin_d6 = 34,  .pin_d7 = 35,
    .pin_vsync = 25,    .pin_href = 23,  .pin_pclk = 22,

    // -- 时钟 --
    .xclk_freq_hz = 20000000,       // 20 MHz XCLK

    // -- 图像参数 --
    .pixel_format  = PIXFORMAT_JPEG, // JPEG 直出 (OV2640 硬件编码)
    .frame_size    = FRAMESIZE_SVGA, // 800×600
    .jpeg_quality  = 12,             // 0(最高)~63(最低)

    // -- 帧缓冲 --
    .fb_count      = 2,              // 双缓冲 (乒乓)
    .fb_location   = CAMERA_FB_IN_PSRAM, // PSRAM
    .grab_mode     = CAMERA_GRAB_WHEN_EMPTY,
};

5. 应用层

5.1 执行流程

app_main()
  │
  ├─ [1] nvs_flash_init()          ← 初始化 NVS 分区
  │
  ├─ [2] esp_camera_init()         ← 初始化 OV2640
  │       ├─ 配置 SCCB (I2C) 引脚
  │       ├─ 配置 DVP 8 位并行数据引脚
  │       ├─ 启动 XCLK 20MHz
  │       ├─ 通过 SCCB 写入 OV2640 寄存器
  │       │   ├─ 设置为 JPEG 模式
  │       │   ├─ 分辨率 SVGA (800×600)
  │       │   ├─ JPEG 质量等级 12
  │       │   └─ 帧率 ~30fps
  │       ├─ 配置 DMA 引擎
  │       └─ 在 PSRAM 中分配 framebuffer (fb_count=2)
  │
  ├─ [3] wifi_init_sta()           ← 初始化 WiFi STA
  │       ├─ esp_netif_init()
  │       ├─ esp_event_loop_create_default()
  │       ├─ esp_wifi_init() + esp_wifi_set_config(SSID, PWD)
  │       ├─ 注册事件回调:
  │       │   ├─ WIFI_EVENT_STA_START → esp_wifi_connect()
  │       │   ├─ WIFI_EVENT_STA_DISCONNECTED → esp_wifi_connect()
  │       │   └─ IP_EVENT_STA_GOT_IP → start_webserver()
  │       └─ esp_wifi_start()
  │
  └─ [4] (等待 WiFi 连接...)
         │
         └─ WiFi 连接成功 → start_webserver()
              ├─ httpd_start() 在端口 80 启动 HTTP 服务
              ├─ 注册 URI: GET /capture → capture_handler
              ├─ 注册 URI: GET /stream  → stream_handler
              └─ 注册 URI: GET /        → index_handler

5.2 三个 HTTP 端点

端点 方法 Content-Type 说明
/ GET text/html 首页:显示实时流 <img src="/stream"> + 拍照按钮
/capture GET image/jpeg 单帧 JPEG 拍照并下载
/stream GET multipart/x-mixed-replace; boundary=frame MJPEG 视频流

6. 数据流详解

6.1 摄像头采集流程

                    OV2640 传感器
                        │
              DVP 8bit 并行总线
              (PCLK + HREF + VSYNC + D0~D7)
                        │
                        ▼
                  ESP32 DMA 引擎
                  (GDMA Channel)
                        │
                        ▼
                   PSRAM 帧缓冲
              ┌────────────────────────┐
              │  fb[0]  │  fb[1]       │  ← 双缓冲乒乓
              │ 150KB   │  150KB       │     JPEG SVGA≈30-150KB
              └────────────────────────┘
                        │
               esp_camera_fb_get()
                        │
                        ▼
               camera_fb_t 结构体
              ┌─────────────────────┐
              │  .buf   → JPEG 数据  │
              │  .len   → 数据长度   │
              │  .width → 800       │
              │  .height→ 600       │
              │  .format→ JPEG      │
              └─────────────────────┘
                        │
              ┌─────────┴──────────┐
              ▼                    ▼
       capture_handler     stream_handler
       (单帧 HTTP 响应)     (MJPEG 循环推送)

6.2 MJPEG 流帧时序

时间 ────────────────────────────────────────────────→

  传感器:         [帧1捕获]        [帧2捕获]        [帧3捕获]
                  │                │                │
  DMA:            │  传输到PSRAM    │                │
                  │                │                │
  fb_get():       ▼ 获取fb        ▼ 获取fb         ▼
                  T0              T1              T2
  HTTP chunk:     [--frame + JPEG][--frame + JPEG][--frame + JPEG]
                  │                │                │
  fb_return():    ▼ 归还fb        ▼                ▼
                  T0+1            T1+1            T2+1
                  
  客户端:         流式渲染帧1     流式渲染帧2      流式渲染帧3
  
  帧间隔: ~50ms (20 FPS, 软件控制 vTaskDelay)

6.3 HTTP MJPEG 流协议

HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame

--frame
Content-Type: image/jpeg
Content-Length: 28456

<binary JPEG data ...>
--frame
Content-Type: image/jpeg
Content-Length: 29102

<binary JPEG data ...>
--frame
Content-Type: image/jpeg
Content-Length: 27634

<binary JPEG data ...>
...

这是 MJPEG over HTTP 的标准协议——浏览器原生支持 <img src="/stream"> 即可显示实时画面,无需 JavaScript。

7. HTTP 接口说明

GET / — 首页

返回一个自包含的 HTML 页面:

  • 嵌入 <img id="stream" src="/stream"> 实时显示视频流
  • 提供 “📸 拍照” 链接到 /capture
  • 提供 “🎥 全屏流” 链接到 /stream
  • 响应式设计,适配手机和桌面

GET /capture — 拍照

  • 调用 esp_camera_fb_get() 获取当前帧
  • 返回 Content-Type: image/jpeg
  • Content-Disposition: inline; filename=capture.jpg 方便保存
  • 每次请求都是独立帧,响应完成后立即归还 framebuffer

GET /stream — 视频流

  • 无限循环获取帧并发送
  • 每帧间隔 50ms 的 vTaskDelay (约 20 FPS)
  • 浏览器端通过 <img> 标签自动递进渲染
  • 断开连接时,httpd_resp_send_chunk 返回错误,循环退出

8. 引脚配置

AI-Thinker ESP32-CAM 标准引脚

功能 GPIO 方向 说明
SCCB (I2C 控制)
SIOD 26 双向 SCCB 数据线
SIOC 27 输出 SCCB 时钟线
DVP 数据
D0 (Y2) 5 输入 数据位 0
D1 (Y3) 18 输入 数据位 1
D2 (Y4) 19 输入 数据位 2
D3 (Y5) 21 输入 数据位 3
D4 (Y6) 36 输入 数据位 4
D5 (Y7) 39 输入 数据位 5
D6 (Y8) 34 输入 数据位 6
D7 (Y9) 35 输入 数据位 7
DVP 同步
PCLK 22 输入 像素时钟
HREF 23 输入 行有效
VSYNC 25 输入 帧同步
控制
XCLK 0 输出 主时钟 20MHz (LEDC)
PWDN 32 输出 摄像头掉电 (低有效)
RESET -1 不使用 (硬件上拉)
外设
LED (闪光灯) 4 输出 高电平点亮

9. 存储器布局

Flash 分区 (单应用模式)

Flash 4MB ────────────────────────────── 0x400000
│  (空闲)                                   │
│  factory 分区 1MB                         │
│  ├── esp32-cam.bin  946 KB (0xEC890)     │  ← 应用固件
│  └── 空闲          78 KB                 │
│                      0x100000 ─────────── 0x200000
│  NVS 分区                                 │
│  └── WiFi 配置、摄像头参数                │
│                      0x9000  ──────────── 0x10000
│  partition-table.bin   3 KB               │
│                      0x8000  ──────────── 0x9000
│  bootloader.bin        26 KB              │
│                      0x1000  ──────────── 0x8000
└────────────────────────────────────────── 0x0000

运行时内存

内部 SRAM (520 KB)
├── 指令 RAM:  128 KB  (IRAM, 缓存 + 驻留代码)
├── 数据 RAM:  384 KB  (DRAM, 堆 + 栈 + .bss + .data)
│   ├── FreeRTOS 堆: ~200 KB
│   ├── WiFi 栈:     ~50 KB
│   ├── LWIP:        ~40 KB
│   └── 其他:        ~94 KB

外部 PSRAM (4 MB)
├── framebuffer[0]:  ~150 KB  (JPEG SVGA 帧)
├── framebuffer[1]:  ~150 KB  (备用帧)
└── 空闲:            ~3.7 MB  (可分配)

10. 构建与烧录

开发环境

# 初始化 IDF 环境 (每次新终端)
. D:\Espressif\frameworks\esp-idf-v5.5.4\export.ps1

# 进入项目目录
cd C:\Users\HUAWEI\.openclaw\workspace\esp32-cam

编译

idf.py build
# 产物: build/esp32-cam.bin (946 KB)
# 首次编译约 3-5 分钟,增量编译 <1 分钟

烧录

# 烧录到 ESP32-CAM (替换 COMx 为实际串口号)
idf.py -p COM3 flash

# 或使用 esptool 手动烧录
python -m esptool --chip esp32 -p COM3 -b 460800 \
  --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 2MB --flash_freq 40m \
  0x1000 build/bootloader/bootloader.bin \
  0x8000 build/partition_table/partition-table.bin \
  0x10000 build/esp32-cam.bin

串口监控

idf.py -p COM3 monitor
# 退出: Ctrl + ]

使用前配置

务必先修改 WiFi SSID 和密码 (文件: main/esp32-cam.c):

#define WIFI_SSID      "你的WiFi名"
#define WIFI_PASSWORD  "你的WiFi密码"

修改后重新编译即可。


附录 A: 项目文件清单

esp32-cam/
├── CMakeLists.txt              # 根 CMake 项目文件
├── sdkconfig                   # 当前 Kconfig 配置 (自动生成)
├── sdkconfig.defaults          # 默认 Kconfig 配置
├── main/
│   ├── CMakeLists.txt          # 主组件 CMake
│   ├── idf_component.yml       # 组件依赖声明 (esp32-camera)
│   └── esp32-cam.c             # 应用代码 (约 310 行)
├── managed_components/         # 组件仓库下载 (自动)
│   ├── espressif__esp32-camera/
│   └── espressif__esp_jpeg/
├── build/                      # 编译产物 (自动)
│   ├── esp32-cam.bin           # 最终固件
│   ├── esp32-cam.elf           # ELF 可执行文件
│   ├── bootloader/             # 二级 bootloader
│   └── partition_table/       # 分区表
└── docs/
    ├── architecture.md         # 架构文档
    ├── architecture.mmd        # 架构 Mermaid 图
    └── dataflow.mmd            # 数据流 Mermaid 图

附录 B: 常见问题

Q: 摄像头初始化失败? A: 检查摄像头排线是否插紧,确认 PSRAM 已使能 (CONFIG_SPIRAM=y)。

Q: 画面颜色偏色/绿屏? A: 检查 XCLK 频率是否稳定 (建议 20MHz),尝试降低帧大小到 QQVGA。

Q: WiFi 连接不上? A: 确认 SSID 和密码正确,ESP32 仅支持 2.4GHz WiFi。

Q: 网页加载但无画面? A: 检查浏览器是否支持 MJPEG (Chrome/Firefox/Safari 均支持),确认 /stream 端点正常响应。

Q: 编译时找不到 esp32-camera 组件? A: 确认网络连接正常,首次编译会自动下载。也可手动 idf.py reconfigure