项目目标
给 ESP32-S3 开发板接上 OV5640 摄像头模组,烧录程序后让摄像头连上 WiFi,手机或电脑用浏览器打开一个网址,就能看到清晰、流畅的实时视频画面。
硬件与工具
硬件
| 项目 | 型号 / 规格 |
|---|---|
| 主控板 | 02073 ESP32-S3 WROOM N16R8 摄像头开发板 |
| 主控芯片 | ESP32-S3-WROOM-1(QFN56,rev v0.2) |
| Flash | 16MB,Quad(4 数据线) |
| PSRAM | 8MB,Octal,AP Memory 3.3V 颗粒 |
| 摄像头模组 | OV5640,型号 FD5640 500W-V11,24-Pin FPC,带自动对焦 |
| USB 接口 | 两个 Type-C:原生 USB-Serial/JTAG + CH343 转 UART0 |
板子把 ESP32-S3 所有 GPIO 引到两排 22Pin 排针(J1/J2),没有专用摄像头座,摄像头靠杜邦线手动接到排针上。
用到的工具
| 工具 | 用途 |
|---|---|
| esptool | 烧录 .bin 固件、查看芯片信息、读取 Flash ID |
| Arduino CameraWebServer | 官方摄像头联网直播示例源码 |
| arduino-cli | 本地重新编译固件(真实WiFi + 原生JPEG) |
| Python + pyserial | 排查"烧录成功但看不到串口输出"的问题 |
| ffmpeg / edge-tts | 剪辑教程视频、生成中文语音旁白 |
代码文件与引脚映射
CameraWebServer.ino— 主程序:初始化摄像头、连接 WiFi、启动网页服务器app_httpd.cpp— 网页服务器实现(视频流、拍照、参数调节接口)camera_pins.h— 各参考板型的摄像头引脚定义camera_index.h— 网页控制界面(压缩打包的 HTML/JS)partitions.csv— 自定义 Flash 分区表
摄像头引脚映射(CAMERA_MODEL_ESP32S3_EYE)
这组引脚正好避开了板子 USB 原生接口(GPIO19/20)和 UART0(GPIO43/44)占用的引脚,没有冲突。
开发板 & 代码要点讲解
ESP32-S3 是什么
ESP32-S3 双核主控,自带 WiFi + 蓝牙,比老款 ESP32 多一组向量指令,适合摄像头采集、网页预览和轻量图像处理这类应用。
PSRAM(8MB)是重点:摄像头一帧图像数据量很大,芯片自带的 320KB 内存放不下,必须靠外扩 PSRAM 做帧缓冲区,代码里 psramFound() 就是在检查这块内存在不在。
两个 USB 口是最大的坑:一个是芯片自带原生 USB,另一个是板上 CH343 芯片转出来的 UART0。这份程序默认走 UART0,插错口的话串口监视器会"完全没反应"——不是乱码,是彻底没输出。
代码里最关键的设置:pixel_format
| 取值 | 行为 | 结果 |
|---|---|---|
| PIXFORMAT_JPEG | 摄像头硬件直接输出压缩好的 JPEG | CPU 几乎不用干活,流畅 |
| PIXFORMAT_RGB565 | 输出原始像素,网页服务器每帧现场软件转码 | 非常吃 CPU,是卡顿根本原因 |
小技巧:主控制页面和视频流分成两个端口——主页面默认端口,视频流单独在 端口 81。想不带设置面板、全屏看画面,直接打开 http://<板子IP>:81/stream 即可。
OV5640 摄像头脚位资料
模组型号 FD5640 500W-V11,24-Pin FPC 排线接口:
| Pin | 信号 | 作用 | Pin | 信号 | 作用 |
|---|---|---|---|---|---|
| 1 | STROBE | 闪光灯触发 | 13 | MCLK | 主时钟 (XCLK) |
| 2 | AGND | 模拟地 | 14 | Y8 | 数据位 |
| 3 | SDA | SCCB 数据(控制参数) | 15 | DGND | 数字地 |
| 4 | AVDD | 模拟电源 | 16 | Y7 | 数据位 |
| 5 | SCL | SCCB 时钟 | 17 | PCLK | 像素时钟 |
| 6 | RESET | 复位 | 18 | Y6 | 数据位 |
| 7 | VSYNC | 帧同步 | 19 | Y2 | 数据位 |
| 8 | PWDN | 掉电控制 | 20 | Y5 | 数据位 |
| 9 | HREF | 行同步 | 21 | Y3 | 数据位 |
| 10 | DVDD | 数字电源 | 22 | Y4 | 数据位 |
| 11 | DOVDD | 数字IO电源 | 23 | AFVDD | 自动对焦电源 |
| 12 | Y9 | 数据位 | 24 | AF-GND | 自动对焦地 |
这套接口叫 DVP(数字视频并口):MCLK 给摄像头"打拍子",摄像头按拍子把每行/每帧图像通过 VSYNC/HREF/PCLK 加上 8 根数据线(Y2~Y9)并行传给主控;SDA/SCL 单独用来"发指令"(调分辨率、亮度等)。
关于 OV5640 画质 / 发热的网上争议
网上对 OV5640 评价两极分化,有人说"720P 都能卡成 PPT,1080P 直接烫手,效果不如 OV2640"。这个争议有依据,但不完全适用于我们的场景:
网上的说法
500万像素 + 自动对焦马达,功耗发热天然更高;esp32-camera 驱动库最初主要针对 OV2640 调优,高分辨率下兼容性没那么成熟,容易卡顿甚至发烫。
我们的实测
全程测试都在 QQVGA / QVGA / VGA 这种中低分辨率,没有拉到 720P 以上,实测流畅清晰;没有做过和 OV2640 的直接对比,也不假设这颗模组一定没问题——如实记录,不下过头的结论。
完整踩坑过程
烧录成功,但看不到任何串口输出
固件烧录校验都通过,但 Python/pyserial 读串口全是空的,一度怀疑摄像头接线不对导致程序卡死。换简单测试程序(WiFiScan)依然没输出,排除了摄像头程序本身的问题。
Serial.print() 其实走 UART0 → CH343 → 另一个 USB 口,跟当时监听的口不是一回事。换插另一个口,问题解决。WiFi 账号密码写死在固件里
固件里是原作者的占位值(sparkleiot / 12345678),板子一直卡在"连接 WiFi",串口反复打印 .....。
验证摄像头画面
在同一 WiFi 下浏览器打开串口打印的 IP,进入摄像头控制页面点击 Start Stream,确认摄像头本身、接线、初始化全部正常。
画面卡顿
固件里 pixel_format 设成了 PIXFORMAT_RGB565,每一帧都要现场用软件转成 JPEG,非常吃 CPU。
PIXFORMAT_JPEG,配合 8MB PSRAM 双缓冲。arduino-esp32 3.x 编译报错
示例代码是配合较老的 2.x 核心写的,用 3.3.10 编译报了两个错:人脸检测头文件 human_face_detect_msr01.hpp 已不随核心库分发;ledcSetup()/ledcAttachPin() 旧版 LEDC API 已被替换。
CONFIG_ESP_FACE_DETECT_ENABLED、CONFIG_LED_ILLUMINATOR_ENABLED 设成 0,跳过这两段代码。正式版:真实 WiFi + 原生 JPEG
重新编译烧录后,实测 VGA(640×480) 分辨率下画面清晰流畅,不再需要"改热点名字"这个临时办法。人像和小字体清晰度也做了实测(如实展示,包括仍然偏糊的部分)。
当前状态
视频与 B 站发布素材
教程视频(配音版)
ESP32S3_OV5640_教程视频_配音版.mp4 — 约 2 分 43 秒,720×1280 竖屏,中文语音旁白(Edge TTS zh-CN-XiaoxiaoNeural),每段视频时长按旁白实际播放时长精确匹配,音画同步。
封面
标题
ESP32-S3 接 OV5640 摄像头教程:配置、引脚、出图全记录(真实踩坑)
简介
用一块 ESP32-S3 开发板接上 OV5640 摄像头模组,做一个能在浏览器里实时看画面的联网摄像头项目。 视频内容: - 板子和摄像头硬件介绍 - OV5640 的 DVP 接口和引脚讲解 - 代码里最关键的设置项(JPEG vs RGB565,直接决定卡不卡) - 真实踩坑过程:串口选错口、WiFi账号写死在固件里、画面卡顿怎么优化 - 从最初的临时方案到最终正式版(真实WiFi+原生JPEG)的完整升级过程 - 人像效果和小字体清晰度实测(如实展示,不回避问题) 完整代码、接线图、详细踩坑记录见评论区置顶。 入门向,有不对的地方欢迎指正~