1. 为什么我最终选了ESP32-CAM做图像传输
第一次接触ESP32-CAM是在做一个远程监控的小项目,需求很朴素:能拍照、能把图片传出来、成本要低、体积要小。翻了一圈方案,树莓派Zero加摄像头模块成本直接飙到两百以上,还不好塞进小外壳里;单独的ESP32开发板加OV2640摄像头模块,接线一堆,PCB面积也大。ESP32-CAM这块板子把ESP32-S芯片、OV2640摄像头、MicroSD卡槽、板载天线全塞进了一个25mm×27mm的板子上,某宝三十块左右就能拿下,性价比确实没得说。
但便宜有便宜的代价。这块板子没有板载USB转串口芯片,烧录程序需要外接USB-TTL模块;供电要求比较挑剔,5V供电不足会导致摄像头初始化失败或者WiFi连接不稳定;引脚复用严重,能用的IO口屈指可数。这些问题我在实际调试中都踩过,后面会一个个展开讲。
这篇文章面向的是有一定嵌入式基础、想快速把ESP32-CAM跑起来的开发者。不管你是用Arduino IDE还是ESP-IDF,我都会把硬件接线、源码实现、常见坑点讲清楚。源码部分我会给出完整的可运行代码,包括摄像头初始化、WiFi连接、HTTP图像传输、以及一个简单的Web服务器实现。你拿到代码改一下WiFi账号密码就能直接用。
核心关键词先摆出来:ESP32-CAM、图像传输、硬件接线、源码、ESP-IDF。这几个词贯穿全文,也是我在调试过程中反复打交道的对象。
2. 硬件接线与供电方案拆解
2.1 ESP32-CAM的引脚分布与功能复用
ESP32-CAM的引脚设计是这块板子最让人头疼的地方。它用的是ESP32-S芯片,本身有34个GPIO,但板子上引出来的可用IO非常有限。原因在于OV2640摄像头占用了大量引脚,MicroSD卡槽又占了一部分,剩下的能自由使用的IO口基本只有GPIO 0、GPIO 1(串口TX)、GPIO 3(串口RX)、GPIO 16,以及GPIO 13、GPIO 12、GPIO 14、GPIO 15这几个。
先看摄像头占用的引脚,这个必须记牢,因为你在接线和写代码时都要避开这些:
| 功能 | GPIO引脚 | 说明 |
|---|---|---|
| XCLK | GPIO 0 | 摄像头主时钟输出 |
| SIOD | GPIO 26 | SCCB数据线 |
| SIOC | GPIO 27 | SCCB时钟线 |
| Y9 | GPIO 35 | 数据位9 |
| Y8 | GPIO 34 | 数据位8 |
| Y7 | GPIO 39 | 数据位7 |
| Y6 | GPIO 36 | 数据位6 |
| Y5 | GPIO 21 | 数据位5 |
| Y4 | GPIO 19 | 数据位4 |
| Y3 | GPIO 18 | 数据位3 |
| Y2 | GPIO 5 | 数据位2 |
| VSYNC | GPIO 25 | 垂直同步信号 |
| HREF | GPIO 23 | 水平参考信号 |
| PCLK | GPIO 22 | 像素时钟 |
这些引脚在摄像头工作时全部被占用,你不能再拿来做其他用途。剩下的可用引脚里,GPIO 1和GPIO 3是串口通信用的,烧录和调试都靠它们。GPIO 0用来控制烧录模式,上电时拉低进入下载模式。GPIO 16可以接LED做状态指示。GPIO 13、12、14、15可以接一些外设,但注意GPIO 12在上电时如果被拉高会导致启动失败,所以最好别用它做输出。
2.2 烧录接线:USB-TTL模块的正确接法
ESP32-CAM没有USB接口,必须通过USB-TTL模块连接电脑烧录。我手头用的是CH340G模块,便宜好用。接线方式如下:
USB-TTL ESP32-CAM 5V ---> 5V GND ---> GND TX ---> U0R (GPIO 3) RX ---> U0T (GPIO 1)这里有个关键点:烧录时必须把GPIO 0拉低。ESP32-CAM板子上有一个微动按键,标着IO0,按住这个键再上电或者按复位键,就进入下载模式了。具体操作顺序是:先按住IO0键不放,然后按一下RST键,松开RST键,再松开IO0键。这时候板子就处于等待烧录的状态。
我一开始不知道这个操作,直接上电烧录,Arduino IDE一直报"Failed to connect to ESP32: Timed out waiting for packet header"。折腾了半小时才想起来GPIO 0的事。这个坑几乎每个新手都会踩,记住就行。
注意:烧录完成后,必须断开GPIO 0的拉低状态(松开按键),再按一次RST键,程序才会正常运行。否则板子会一直停留在下载模式。
2.3 供电方案:为什么你的摄像头总是初始化失败
供电是ESP32-CAM最容易被忽视的问题。板子上有一个AMS1117-3.3稳压芯片,输入5V输出3.3V给ESP32和摄像头供电。问题在于,OV2640摄像头在启动瞬间的电流峰值可以达到200mA以上,加上ESP32本身WiFi工作时的电流,整体峰值可能超过500mA。如果你用电脑USB口直接供电,电流可能不够,导致摄像头初始化失败,串口打印"Camera init failed with error 0x20001"。
我的解决方案是:用独立的5V/2A电源适配器供电,或者用充电宝的5V/2A输出口。如果一定要用USB-TTL模块供电,确保模块的5V输出能力足够,CH340G模块的5V输出通常只有500mA左右,勉强够用但不稳定。
另外,板子上有一个红色LED接在GPIO 33上,作为闪光灯使用。这个LED在亮起时电流也不小,如果不需要闪光灯功能,建议在代码里把它关掉,省电。
还有一个细节:ESP32-CAM的5V输入引脚旁边有一个二极管,会有约0.3V的压降。如果你用5V供电,实际到达稳压芯片的电压可能只有4.7V左右。虽然AMS1117的压差要求是1.1V,4.7V输入输出3.3V是够的,但如果电源本身输出偏低(比如4.8V),就可能出问题。所以尽量选输出稳定在5.1V以上的电源。
3. 源码实现:从摄像头初始化到图像传输
3.1 开发环境选择:Arduino IDE还是ESP-IDF
ESP32-CAM的开发环境主要有两种:Arduino IDE和ESP-IDF。Arduino IDE上手快,库封装好,适合快速验证;ESP-IDF更底层,配置灵活,适合产品级开发。我两个都用过,这里分别说一下。
Arduino IDE的配置步骤:先在首选项里添加ESP32的板管理URL,然后在开发板管理器里安装"esp32 by Espressif Systems"。安装完成后,选择开发板为"AI Thinker ESP32-CAM"。需要注意的是,Arduino IDE默认的ESP32分区方案可能不够用,需要在工具菜单里把"Partition Scheme"改成"Huge APP (3MB No OTA)",否则编译会报空间不足。
ESP-IDF的配置稍微复杂一些。你需要先安装ESP-IDF Tools Installer,然后通过idf.py创建项目。ESP-IDF的好处是你可以直接调用esp32-camera组件,配置项更细粒度。比如你可以调整摄像头的帧率、分辨率、JPEG质量等参数,Arduino IDE里这些参数虽然也能改,但不如ESP-IDF直观。
我个人的建议是:如果你只是想快速跑通图像传输,用Arduino IDE就够了;如果你要做产品或者需要精细控制,上ESP-IDF。下面的源码我以Arduino IDE为主,因为它的代码更简洁,适合大多数读者。
3.2 摄像头初始化代码详解
摄像头初始化的核心是配置camera_config_t结构体。这个结构体定义了摄像头的引脚映射、时钟频率、分辨率、像素格式等参数。下面是我实际使用的配置:
#include "esp_camera.h" // ESP32-CAM的引脚定义 #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #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 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 void setupCamera() { 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; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; config.fb_count = 1; esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } }这里有几个参数需要重点解释。xclk_freq_hz是摄像头的主时钟频率,默认是20MHz。如果你发现图像有横纹或者颜色异常,可以尝试降到10MHz。frame_size是分辨率,可选的有FRAMESIZE_QVGA(320×240)、FRAMESIZE_VGA(640×480)、FRAMESIZE_SVGA(800×600)、FRAMESIZE_UXGA(1600×1200)等。分辨率越高,图像越大,传输时间越长。jpeg_quality是JPEG压缩质量,范围是0-63,数值越小质量越高、文件越大。我一般用12,在SVGA分辨率下单张图片大约30-50KB,传输时间在1秒左右。
fb_count是帧缓冲数量,设为1时只分配一个缓冲区,内存占用小但连续拍照时可能丢帧;设为2时可以双缓冲,拍照更流畅但内存占用翻倍。ESP32-CAM的PSRAM有4MB(如果板子上焊了PSRAM的话),设2完全没问题。但注意,有些便宜的ESP32-CAM板子没有焊PSRAM,这时候fb_count只能设1,而且分辨率不能超过VGA,否则内存不够。
3.3 WiFi连接与HTTP图像传输
WiFi连接部分比较标准,用WiFi.h库就行。我把它封装成一个函数,带重试机制:
#include <WiFi.h> const char* ssid = "你的WiFi名称"; const char* password = "你的WiFi密码"; void connectWiFi() { WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); int retry = 0; while (WiFi.status() != WL_CONNECTED && retry < 20) { delay(500); Serial.print("."); retry++; } if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWiFi connected"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println("\nWiFi connection failed"); } }图像传输我用的是HTTP协议,ESP32-CAM作为HTTP服务器,客户端通过浏览器访问ESP32-CAM的IP地址,就能看到实时图像。核心代码如下:
#include <WebServer.h> WebServer server(80); void handleCapture() { camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { server.send(500, "text/plain", "Camera capture failed"); return; } server.send_P(200, "image/jpeg", (const char *)fb->buf, fb->len); esp_camera_fb_return(fb); } void setupServer() { server.on("/capture", handleCapture); server.begin(); Serial.println("HTTP server started"); } void loop() { server.handleClient(); }这段代码的逻辑很简单:当客户端访问/capture路径时,调用esp_camera_fb_get()获取一帧图像,然后通过server.send_P()把JPEG数据发送出去。send_P的P表示数据在Flash中,直接发送可以节省内存。发送完成后必须调用esp_camera_fb_return()释放帧缓冲,否则下次拍照会失败。
如果你想要更流畅的视频流,可以用MJPEG格式。MJPEG的本质是把多张JPEG图片连续发送,每张图片之间用特定的boundary分隔。浏览器收到后会自动渲染成视频流。实现起来也不复杂,就是在HTTP响应头里设置Content-Type: multipart/x-mixed-replace; boundary=frame,然后循环发送JPEG帧。不过ESP32-CAM的WiFi带宽有限,MJPEG流在SVGA分辨率下大概只能跑到5-10帧每秒,VGA分辨率下能到15帧左右。
3.4 完整可运行源码
把上面的代码整合起来,加上必要的头文件和初始化逻辑,就是一套完整的可运行源码。我把它放在这里,你直接复制到Arduino IDE里,改一下WiFi账号密码就能用:
#include "esp_camera.h" #include <WiFi.h> #include <WebServer.h> // 引脚定义 #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #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 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 const char* ssid = "你的WiFi名称"; const char* password = "你的WiFi密码"; WebServer server(80); void setupCamera() { 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; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; config.fb_count = 1; esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } } void connectWiFi() { WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); int retry = 0; while (WiFi.status() != WL_CONNECTED && retry < 20) { delay(500); Serial.print("."); retry++; } if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWiFi connected"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println("\nWiFi connection failed"); } } void handleCapture() { camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { server.send(500, "text/plain", "Camera capture failed"); return; } server.send_P(200, "image/jpeg", (const char *)fb->buf, fb->len); esp_camera_fb_return(fb); } void setup() { Serial.begin(115200); setupCamera(); connectWiFi(); server.on("/capture", handleCapture); server.begin(); Serial.println("HTTP server started"); } void loop() { server.handleClient(); }烧录进去之后,打开串口监视器,波特率115200,你会看到ESP32-CAM打印出获取到的IP地址。然后在浏览器里输入http://[IP地址]/capture,就能看到一张实时拍摄的JPEG图片。刷新页面就会重新拍照。
4. 踩坑实录与排查技巧
4.1 摄像头初始化失败的几种原因
摄像头初始化失败是最常见的问题,串口会打印Camera init failed with error 0x20001或者0x20002。这个错误码的含义是ESP_ERR_CAMERA_NOT_DETECTED,意思是摄像头没有检测到。原因通常有三个:
第一,供电不足。前面说过,摄像头启动瞬间电流大,如果电源带不动,就会初始化失败。解决办法是换一个输出能力更强的5V电源,或者在5V和GND之间并联一个100μF以上的电解电容,起到缓冲作用。
第二,排线接触不良。ESP32-CAM的摄像头是通过一个24pin的FPC排线连接的,排线插反或者没插紧都会导致检测失败。检查方法是把排线拔下来,看金手指是否干净,然后重新插紧,确保卡扣扣好。注意排线的蓝色加强板方向,一般是朝外的。
第三,引脚定义错误。如果你用的不是AI Thinker的ESP32-CAM,而是其他厂家的兼容板,引脚定义可能不一样。这时候需要查你手上板子的原理图,确认摄像头引脚映射。我遇到过一块板子,它的Y2-Y9数据线顺序和AI Thinker的完全相反,改了半天代码才跑通。
4.2 WiFi连接不稳定与图像传输中断
WiFi连接不稳定表现为:串口打印"WiFi connected"后,过一会儿又断开;或者浏览器访问/capture时经常超时。这个问题通常和电源质量、天线布局、WiFi信道拥堵有关。
电源方面,WiFi发射时电流会突然增大,如果电源响应速度慢,电压会瞬间跌落,导致WiFi模块复位。解决办法还是加强供电,或者在电源引脚附近加一个470μF的电容。
天线方面,ESP32-CAM用的是板载PCB天线,增益不高。如果板子放在金属外壳里,或者周围有金属物体,信号会严重衰减。尽量让板子远离金属,或者把板子放在塑料外壳里。
WiFi信道方面,2.4GHz频段很拥挤,尤其是周围有很多路由器的时候。你可以在路由器设置里把信道固定为1、6、11中的一个,减少干扰。另外,ESP32-CAM只支持2.4GHz WiFi,不支持5GHz,所以你的路由器必须开启2.4GHz频段。
图像传输中断还有一个可能的原因是帧缓冲没有及时释放。如果你在代码里调用了esp_camera_fb_get()但忘记调用esp_camera_fb_return(),帧缓冲会被耗尽,后续拍照全部失败。这个错误很隐蔽,因为串口不会报错,只是图像传输突然停止。检查代码里每一处fb_get是否都有对应的fb_return。
4.3 图像质量差与颜色异常的调整方法
图像质量差表现为:画面模糊、噪点多、颜色偏绿或偏紫。原因可能是镜头没对焦、光线不足、或者摄像头参数配置不当。
镜头对焦:OV2640的镜头是可以旋转的,出厂时通常对焦在无穷远。如果你拍近处的物体,需要手动旋转镜头调整焦距。方法是:把摄像头对准一个近处的物体,一边旋转镜头一边观察图像,直到清晰为止。注意旋转时要轻,镜头螺纹是塑料的,用力过猛会滑丝。
光线不足:OV2640在低照度下噪点会明显增加。如果应用场景光线不好,可以考虑加一个补光灯,或者换用OV5640摄像头(但ESP32-CAM的引脚定义需要改)。另外,可以在代码里调整config.jpeg_quality,降低质量值(提高压缩率)可以减少噪点,但画面会变模糊。
颜色异常:如果画面偏绿,通常是白平衡没调好。ESP32-CAM的摄像头驱动支持自动白平衡,但有时候会失效。你可以在初始化后手动设置白平衡模式:
sensor_t *s = esp_camera_sensor_get(); s->set_whitebal(s, 1); // 开启自动白平衡 s->set_awb_gain(s, 1); // 开启自动白平衡增益 s->set_wb_mode(s, 0); // 0=自动, 1=晴天, 2=阴天, 3=办公室, 4=家里如果偏紫,可能是RGB增益不对,可以尝试:
s->set_gain_ctrl(s, 1); // 开启自动增益 s->set_exposure_ctrl(s, 1); // 开启自动曝光4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 串口无输出 | 供电不足或串口接线错误 | 测量5V引脚电压,检查TX/RX是否交叉 | 换电源,确认TX接U0R、RX接U0T |
| 烧录失败 | GPIO 0未拉低 | 检查烧录时是否按住IO0键 | 按住IO0再按RST,进入下载模式 |
| 摄像头初始化失败 | 供电不足或排线松动 | 串口打印错误码0x20001 | 加强供电,重新插拔排线 |
| WiFi连不上 | 路由器不支持2.4GHz或密码错误 | 检查路由器频段设置 | 开启2.4GHz频段,确认密码 |
| 图像传输超时 | 帧缓冲未释放或WiFi信号弱 | 检查代码中fb_get/fb_return配对 | 补上fb_return,靠近路由器 |
| 图像偏绿 | 白平衡失效 | 观察画面颜色 | 手动设置白平衡模式 |
| 图像模糊 | 镜头未对焦 | 旋转镜头观察变化 | 手动调整焦距 |
| 板子发热严重 | 电源电压过高或短路 | 测量5V引脚电压 | 确保5V供电,检查有无短路 |
4.5 几个容易被忽略的实操细节
第一个细节:ESP32-CAM的GPIO 0在烧录时被拉低,但烧录完成后如果GPIO 0仍然被拉低,程序不会运行。所以烧录完成后一定要松开IO0键,再按一次RST键。我见过有人烧录完直接拔线,结果板子一直不工作,以为是代码问题,其实是GPIO 0还处于拉低状态。
第二个细节:Arduino IDE的串口监视器波特率要设为115200,和代码里的Serial.begin(115200)一致。如果波特率不对,串口输出会是乱码。另外,ESP32-CAM启动时会输出一些bootloader信息,这些信息波特率是74880,所以你会看到一串乱码,这是正常的,不用管。
第三个细节:如果你用ESP-IDF开发,menuconfig里的Component config -> ESP32-specific -> Support for external, SPI-connected RAM要打开,否则PSRAM无法使用,分辨率只能设到VGA。Arduino IDE里默认是打开的,但如果你手动改过分区方案,可能会被关掉。
第四个细节:ESP32-CAM的板载LED接在GPIO 33上,高电平点亮。如果你不需要闪光灯,可以在代码里把GPIO 33设为低电平,省电的同时也减少发热。但注意,GPIO 33同时也是摄像头的XCLK引脚?不是,XCLK是GPIO 0,GPIO 33是独立的LED控制引脚,可以放心操作。
第五个细节:如果你要把ESP32-CAM装进外壳里,注意留出摄像头的开孔和散热孔。ESP32-CAM工作时发热不小,尤其是WiFi传输时,芯片温度可以到60度以上。如果完全密封,可能会过热降频甚至死机。我一般会在外壳上开几个小孔,或者加一个小散热片。
5. 图像传输的进阶玩法与扩展思路
5.1 从HTTP到RTSP:更低延迟的传输方案
HTTP传输的延迟其实不低,因为每次请求都要重新建立TCP连接,而且JPEG编码本身也有延迟。如果你需要更低的延迟,可以考虑RTSP协议。ESP32-CAM上跑RTSP需要额外的库,比如Micro-RTSP,它可以把摄像头的JPEG帧封装成RTP包,通过UDP发送。延迟可以降到200ms以内,适合做实时监控。
不过Micro-RTSP的配置比较复杂,需要自己处理RTP打包和RTCP反馈。而且UDP在丢包时会导致画面花屏,需要权衡。我试过在局域网内跑,效果还不错,但跨网络就不太稳定了。
5.2 图像存储:SD卡与云端上传
ESP32-CAM板子上有一个MicroSD卡槽,可以插卡存储图像。使用SD卡需要调用SD_MMC库,注意SD卡和摄像头共用一些引脚,但ESP32-CAM的设计已经做了分时复用,不会冲突。存储图像的好处是可以做离线记录,比如定时拍照存档。
云端上传的话,可以用HTTP POST把JPEG数据发送到服务器。我试过上传到自建的Python Flask服务器,每秒能传2-3张SVGA图片。如果要用云服务,注意选择支持HTTP上传的,不要用需要复杂认证的。上传时建议加上时间戳和设备ID,方便后续检索。
5.3 低功耗优化:电池供电的可行性
ESP32-CAM用电池供电是可行的,但需要做功耗优化。WiFi传输时电流在200mA左右,摄像头工作时也在100mA以上。如果用2000mAh的锂电池,大概能撑5-6小时。优化方法包括:降低分辨率、降低帧率、使用深度睡眠模式、关闭不必要的LED。
深度睡眠模式下,ESP32-CAM的电流可以降到10μA以下,但唤醒后需要重新初始化摄像头和WiFi,启动时间大约2-3秒。如果做定时拍照(比如每10分钟拍一张),平均功耗可以降到1mA以下,电池能撑几个月。
5.4 安全加固:别让你的摄像头裸奔
ESP32-CAM的HTTP服务器默认没有认证,任何人知道IP地址就能访问。如果你把它放在公网上,必须加认证。最简单的做法是HTTP Basic Auth,在server.on()之前加一个认证检查:
server.on("/capture", []() { if (!server.authenticate("admin", "password")) { return server.requestAuthentication(); } handleCapture(); });另外,建议把HTTP换成HTTPS,但ESP32-CAM的算力有限,HTTPS握手会比较慢。如果只是局域网使用,HTTP加认证就够了。如果一定要公网访问,建议在前面加一个反向代理,由代理服务器处理HTTPS和认证。
6. 我在这块板子上踩过的坑和最终体会
ESP32-CAM这块板子,说它好用是真的好用,三十块钱能跑图像传输,性价比没得挑;说它坑多也是真的坑多,供电、烧录、引脚复用、内存限制,每一个都能让你折腾半天。我前前后后用了大概十几块ESP32-CAM,有的跑在Arduino IDE上,有的跑在ESP-IDF上,还有的跑在MicroPython上。踩过的坑总结下来就是几句话:供电要足、烧录要拉低GPIO 0、帧缓冲要释放、WiFi要2.4GHz、分辨率别贪高。
如果你刚开始玩ESP32-CAM,我建议先从QVGA分辨率跑通,确认硬件和代码都没问题,再逐步提高分辨率。不要一上来就设UXGA,那样大概率会失败,而且失败了你也不知道是分辨率的问题还是其他问题。调试的时候串口打印是你的好朋友,每一步都加Serial.println,把关键参数打出来,比盲猜快得多。
最后再分享一个小技巧:如果你手头没有USB-TTL模块,可以用另一块ESP32开发板来做串口转发。把ESP32开发板的TX/RX和ESP32-CAM的U0R/U0T交叉连接,然后通过ESP32开发板的USB口烧录。这个方法我应急用过几次,虽然麻烦点,但能解决问题。