ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP32-CAM图像传输实战:ESP-IDF环境搭建、接线与MJPEG流实现

ESP32-CAM图像传输实战:ESP-IDF环境搭建、接线与MJPEG流实现 1. 为什么我最终选了ESP32-CAM做图像传输先说结论如果你需要一个成本控制在五十块以内、能独立完成拍照并传图的嵌入式方案ESP32-CAM目前仍然是性价比最能打的选择之一。它把ESP32-S芯片、OV2640摄像头模组、TF卡槽、板载天线塞进了一块不到三厘米宽的小板子上官方模组价格常年稳定在三十到五十元区间。对比树莓派加摄像头动辄两三百的方案ESP32-CAM在“只做图像采集与传输”这件事上几乎没有对手。但便宜有便宜的代价。这块板子有几个先天限制你必须提前知道第一它没有板载USB转串口芯片烧录必须外接USB-TTL模块第二GPIO0在启动时决定芯片进入下载模式还是运行模式接线不对就烧不进去第三摄像头和SD卡共用部分引脚同时用会冲突第四供电不足时图像会出现花屏、条纹甚至传输中断。这些坑我在实际调试中全部踩过一遍后面会逐个拆解。这篇文章面向的读者是有基本C语言基础、玩过Arduino或ESP-IDF、想快速把ESP32-CAM跑起来做图像传输的开发者。我会从硬件接线讲起到ESP-IDF环境搭建、源码结构分析、图像传输协议选择再到我实际踩过的坑和排查过程最后给出一套可以直接编译运行的完整源码。整套流程我在Windows和Linux两个平台都验证过下面提到的每个参数和接线都是实测可用的。关键词里提到了ESP-IDF所以本文以ESP-IDF为主要开发框架而不是Arduino IDE。原因很简单ESP-IDF对摄像头的底层控制更精细能直接操作DMA缓冲区传输大尺寸图像时帧率更稳定而且官方维护的esp32-camera组件更新更及时。Arduino虽然上手快但在图像传输这种对内存和带宽敏感的场景下ESP-IDF的优势非常明显。2. 硬件接线一根线接错就白忙活2.1 核心引脚定义与接线表ESP32-CAM的引脚布局比较紧凑第一次拿到手很容易接错。我把关键引脚和对应的USB-TTL接线整理成下表这张表是我反复验证过的照着接不会出问题。ESP32-CAM引脚连接到USB-TTL说明5V5V供电必须接5V3.3V供电不足GNDGND共地必须接U0R (GPIO3)TX芯片接收接TTL的TXU0T (GPIO1)RX芯片发送接TTL的RXGPIO0GND烧录时必须接地运行时要断开这里最容易出错的是U0R和U0T的交叉接法。很多人习惯性地把TX接TX、RX接RX结果串口毫无反应。记住一个原则芯片的接收端要接对方的发送端芯片的发送端要接对方的接收端。U0R是芯片的接收引脚所以接TTL模块的TXU0T是芯片的发送引脚接TTL模块的RX。GPIO0是模式选择引脚。上电瞬间如果GPIO0被拉低芯片进入下载模式等待接收固件如果GPIO0悬空或拉高芯片进入正常运行模式执行已烧录的程序。所以烧录时把GPIO0接到GND烧录完成后断开GPIO0再按一下复位键程序才会正常运行。这个操作顺序不能颠倒。2.2 供电问题90%的诡异现象都出在这里我一开始用USB-TTL模块自带的3.3V给ESP32-CAM供电结果摄像头初始化一直失败串口打印“cam_hal: Failed to get frame”。换成5V供电后问题立刻消失。原因在于OV2640摄像头模组在初始化瞬间的峰值电流可以达到200mA以上加上ESP32本身WiFi传输时的电流波动3.3V线性稳压器根本扛不住这个瞬时负载电压被拉低导致摄像头复位。正确的供电方案有两种一是用USB-TTL模块的5V引脚直接供电前提是你的TTL模块5V输出能力足够至少500mA二是单独用一个5V电源给ESP32-CAM供电USB-TTL只接GND、TX、RX三根线不接5V。第二种方案更稳定尤其是在WiFi传输图像的时候。注意如果你用的是CH340或CP2102这类常见USB-TTL模块它们的5V引脚通常直接从USB口取电电流能力取决于电脑USB口的输出。台式机后置USB口一般能提供500mA以上笔记本的USB口可能只有300mA左右。如果传输大图时频繁断连优先怀疑供电。2.3 摄像头模组排线的方向OV2640摄像头通过一根24pin的FPC排线连接到ESP32-CAM。排线的蓝色加强板那一面朝向摄像头模组的背面金手指朝向板子的连接器。插反了不会烧但摄像头完全没反应。我第一次插的时候没注意方向折腾了半小时才发现是排线插反了。插好之后把连接器的黑色卡扣压紧确保排线不会松动。3. ESP-IDF环境搭建与工程配置3.1 安装ESP-IDF的两种方式ESP-IDF的安装方式主要有两种官方安装器和手动Git克隆。官方安装器适合新手它会自动下载工具链、Python环境和依赖包一步到位。手动克隆适合需要切换版本或定制组件的场景。官方安装器在乐鑫官网可以下载Windows版是一个exeLinux和macOS是sh脚本。安装过程中会让你选择ESP-IDF版本我建议选v5.1或v5.2这两个版本对esp32-camera组件的支持最完善。安装完成后安装器会提供一个“ESP-IDF PowerShell”或“ESP-IDF Terminal”的快捷方式所有编译命令都要在这个终端里执行因为它已经设置好了环境变量。如果你习惯手动安装流程大致是克隆esp-idf仓库切换到指定版本标签运行install.sh安装工具链然后运行export.sh导出环境变量。手动安装的好处是你可以精确控制每个组件的位置方便后续修改源码。3.2 创建工程与添加摄像头组件ESP-IDF的工程结构比较固定一个典型的图像传输工程目录如下esp32cam_stream/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ └── main.c └── components/ └── esp32-camera/在工程根目录的CMakeLists.txt里设置项目名称和包含的组件目录。main目录下的CMakeLists.txt里注册源文件并通过REQUIRES指定依赖的组件这里必须加上esp32-camera和nvs_flash。esp32-camera组件需要单独克隆到components目录下。这个组件是乐鑫官方维护的封装了OV2640、OV3660、OV5640等多款摄像头的驱动。克隆命令是git clone https://github.com/espressif/esp32-camera.git components/esp32-camera克隆完成后不需要额外配置CMake会自动识别components目录下的组件。这里有个细节esp32-camera组件内部依赖esp32-camera的Kconfig配置你可以在menuconfig里调整摄像头的默认参数比如XCLK频率、帧缓冲区数量等。3.3 menuconfig里必须改的几个参数运行idf.py menuconfig进入配置界面有几个参数必须调整否则图像传输会出问题。第一个是Component config - ESP32-specific - CPU frequency默认是160MHz建议改成240MHz。图像编码和WiFi传输都是计算密集型任务240MHz能明显提升帧率。第二个是Component config - Camera configuration - Number of frame buffers默认是1建议改成2。双缓冲可以让摄像头在传输当前帧的同时采集下一帧避免丢帧。代价是占用更多PSRAM但ESP32-CAM通常带4MB PSRAM完全够用。第三个是Component config - ESP32-specific - Support for external, SPI-connected RAM确保这个选项是开启的。ESP32-CAM的PSRAM是必须启用的否则大尺寸图像的帧缓冲区根本分配不出来。第四个是Partition Table默认的单应用分区可能不够用建议改成“Custom partition table CSV”或者选择“Minimal custom partition table”。图像传输的固件通常在1MB左右默认的1MB应用分区刚好卡在边缘稍微加点功能就编译不过。4. 源码结构拆解从摄像头初始化到图像传输4.1 摄像头初始化的关键参数摄像头初始化的核心是填充一个camera_config_t结构体然后调用esp_camera_init。这个结构体里的每个字段都影响最终图像的质量和传输效率。camera_config_t config { .pin_pwdn -1, .pin_reset -1, .pin_xclk 0, .pin_sscb_sda 26, .pin_sscb_scl 27, .pin_d7 35, .pin_d6 34, .pin_d5 39, .pin_d4 36, .pin_d3 21, .pin_d2 19, .pin_d1 18, .pin_d0 5, .pin_vsync 25, .pin_href 23, .pin_pclk 22, .xclk_freq_hz 20000000, .ledc_timer LEDC_TIMER_0, .ledc_channel LEDC_CHANNEL_0, .pixel_format PIXFORMAT_JPEG, .frame_size FRAMESIZE_SVGA, .jpeg_quality 12, .fb_count 2, .fb_location CAMERA_FB_IN_PSRAM, .grab_mode CAMERA_GRAB_WHEN_EMPTY, };pin_pwdn和pin_reset设为-1是因为ESP32-CAM板子上这两个引脚没有引出摄像头一直处于工作状态。xclk_freq_hz设为20MHz是OV2640的推荐值设太高会导致图像噪点增加设太低帧率上不去。pixel_format选JPEG而不是RGB565这是图像传输场景下的关键决策。JPEG格式下OV2640内部直接输出压缩后的图像数据ESP32只需要把JPEG数据搬运到网络缓冲区CPU占用极低。如果选RGB565ESP32需要自己压缩或者直接传原始数据SVGA分辨率下一帧就是600KBWiFi传输根本扛不住。JPEG格式下同样分辨率一帧只有30-50KB传输效率提升十倍以上。frame_size选FRAMESIZE_SVGA800x600这是清晰度和传输速度的平衡点。再往上到UXGA1600x1200JPEG数据量会翻倍帧率降到个位数。再往下到VGA640x480清晰度又不够用。SVGA在室内光照充足的情况下配合jpeg_quality12单帧大约40KBWiFi传输能稳定在10-15帧每秒。fb_count设为2启用双缓冲fb_location设为CAMERA_FB_IN_PSRAM确保帧缓冲区分配在PSRAM里不占用宝贵的内部RAM。grab_mode设为CAMERA_GRAB_WHEN_EMPTY表示当所有帧缓冲区都空的时候才采集新帧避免覆盖还没发送完的数据。4.2 WiFi连接与传输协议选择图像传输的协议选择直接决定了实现的复杂度和最终效果。常见的方案有三种HTTP MJPEG流、WebSocket推送、TCP裸流。我三种都试过最终选了HTTP MJPEG。HTTP MJPEG的方案是ESP32-CAM作为HTTP服务器在特定路径上返回multipart/x-mixed-replace类型的数据流每一帧JPEG图像作为一个part发送。浏览器直接访问这个路径就能看到实时画面不需要任何插件。优点是实现简单、兼容性好、调试方便缺点是延迟比WebSocket高一些大约在200-500ms。WebSocket方案需要ESP32-CAM作为客户端或服务端维护长连接把JPEG帧作为二进制消息推送。延迟可以压到100ms以内但需要自己写前端接收和渲染逻辑调试起来麻烦。TCP裸流方案性能最好但需要自己定义帧边界协议而且浏览器不能直接访问必须写客户端程序。对于大多数应用场景HTTP MJPEG的延迟完全够用而且开发效率最高。下面重点讲这个方案的实现。WiFi连接部分用ESP-IDF的esp_wifi库配置成STA模式连接指定的AP。关键点是WiFi的省电模式必须关闭否则WiFi模块会周期性休眠导致图像传输卡顿。调用esp_wifi_set_ps(WIFI_PS_NONE)关闭省电模式。wifi_config_t wifi_config { .sta { .ssid 你的WiFi名称, .password 你的WiFi密码, .threshold.authmode WIFI_AUTH_WPA2_PSK, }, }; esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(WIFI_IF_STA, wifi_config); esp_wifi_start(); esp_wifi_set_ps(WIFI_PS_NONE);4.3 HTTP服务器与MJPEG流的核心逻辑HTTP服务器的实现基于esp_http_server组件。核心是注册一个URI处理函数在函数里不断获取摄像头帧并通过httpd_resp_send_chunk发送。static esp_err_t stream_handler(httpd_req_t *req) { camera_fb_t *fb NULL; esp_err_t res ESP_OK; char part_buf[64]; res httpd_resp_set_type(req, multipart/x-mixed-replace;boundaryframe); if (res ! ESP_OK) return res; while (true) { fb esp_camera_fb_get(); if (!fb) { res ESP_FAIL; break; } size_t hlen snprintf(part_buf, 64, \r\n--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n, fb-len); res httpd_resp_send_chunk(req, part_buf, hlen); if (res ESP_OK) { res httpd_resp_send_chunk(req, (const char *)fb-buf, fb-len); } if (res ESP_OK) { res httpd_resp_send_chunk(req, \r\n, 2); } esp_camera_fb_return(fb); if (res ! ESP_OK) break; } return res; }这段代码有几个关键点。multipart/x-mixed-replace的boundary必须和每个part之间的分隔符一致这里用的是frame。每个part的头部必须包含Content-Type和Content-Length浏览器才能正确解析每一帧。esp_camera_fb_get获取一帧后必须调用esp_camera_fb_return归还帧缓冲区否则缓冲区很快就会被耗尽摄像头停止工作。httpd_resp_send_chunk是分块发送每次发送一小段数据适合流式传输。如果一次性发送整帧需要分配一个和帧大小相同的缓冲区在PSRAM紧张的时候容易失败。注册URI处理函数httpd_uri_t stream_uri { .uri /stream, .method HTTP_GET, .handler stream_handler, .user_ctx NULL }; httpd_register_uri_handler(server, stream_uri);启动服务器后浏览器访问http://ESP32-CAM的IP/stream就能看到实时画面。5. 踩坑全记录从花屏到断连的排查过程5.1 图像花屏与条纹PSRAM配置的坑第一次跑通代码后串口打印一切正常WiFi也连上了但浏览器里的画面全是绿色条纹和噪点。我一开始怀疑是摄像头模组坏了换了一个模组问题依旧。后来查资料发现这是PSRAM没有正确启用导致的。ESP32-CAM的PSRAM是通过SPI接口外挂的需要在menuconfig里启用Support for external, SPI-connected RAM并且选择正确的SPI模式。默认的SPI模式可能和板子上的PSRAM芯片不匹配。我试了QIO和DIO两种模式最终DIO模式下PSRAM识别正常图像花屏消失。具体操作menuconfig - Component config - ESP32-specific - SPI RAM config - Mode (QUAD/OCT) of SPI RAM chip in use选“DIO”或“QIO”然后看串口启动日志里有没有spiram: Found 4MB SPI RAM的打印。如果没有这行日志说明PSRAM没被识别图像传输必然出问题。5.2 传输几秒后断连帧缓冲区泄漏第二个坑是浏览器打开流之后画面能显示两三秒然后卡死串口打印cam_hal: FB-OVF帧缓冲区溢出。这个问题的根因是帧缓冲区没有及时归还。我检查代码发现在httpd_resp_send_chunk返回错误的时候我直接break跳出了循环但没有调用esp_camera_fb_return。这样每出错一次就泄漏一个帧缓冲区fb_count2的情况下两次错误后缓冲区就耗尽了。修复方法是在break之前确保调用esp_camera_fb_return(fb)。更稳妥的做法是用goto或者把归还操作放在循环末尾的统一位置。修改后的逻辑是无论发送成功还是失败都先归还帧缓冲区再判断是否继续循环。5.3 WiFi传输卡顿省电模式和信道干扰第三个坑是画面能显示但帧率极低大约每秒只有两三帧而且经常卡顿。排查过程分两步先确认摄像头本身的采集帧率再确认WiFi传输的带宽。在代码里加了一个计时打印发现esp_camera_fb_get的调用间隔大约是70ms也就是摄像头本身能跑14帧每秒说明采集端没问题。那瓶颈就在WiFi传输。检查代码发现没有关闭WiFi省电模式调用esp_wifi_set_ps(WIFI_PS_NONE)后帧率立刻提升到10帧以上。另外WiFi信道干扰也会影响传输稳定性。我用手机上的WiFi分析仪看了一下周围有十几个AP都在信道6上而我的路由器默认也是信道6。把路由器切换到信道1或信道11后传输明显更流畅。如果你在办公室或公寓这种AP密集的环境里调试信道选择值得花几分钟优化。5.4 烧录失败GPIO0和复位时序第四个坑是烧录时idf.py flash一直报“Failed to connect to ESP32: Timed out waiting for packet header”。这个问题几乎都是GPIO0的时序不对。正确的烧录流程是先把GPIO0接到GND然后给ESP32-CAM上电或者按一下复位键再执行idf.py flash。烧录完成后断开GPIO0再按一次复位键程序才会运行。我一开始是先上电再接GPIO0芯片已经进入运行模式了这时候再拉低GPIO0不会切换到下载模式必须复位才能重新采样GPIO0的电平。还有一个细节有些USB-TTL模块的DTR和RTS引脚会自动控制ESP32的复位和GPIO0但ESP32-CAM板子上没有引出这两个信号所以只能手动操作。如果你经常烧录可以在GPIO0和GND之间焊一个按钮按一下就能进入下载模式比插拔杜邦线方便得多。6. 完整可运行源码与实测效果6.1 工程文件结构完整的工程包含以下文件esp32cam_stream/ ├── CMakeLists.txt ├── sdkconfig.defaults ├── main/ │ ├── CMakeLists.txt │ └── main.c └── components/ └── esp32-camera/ (从官方仓库克隆)根目录的CMakeLists.txtcmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(esp32cam_stream)main/CMakeLists.txtidf_component_register(SRCS main.c INCLUDE_DIRS . REQUIRES esp32-camera nvs_flash esp_http_server)sdkconfig.defaults里预设关键参数避免每次menuconfig手动改CONFIG_ESP32_SPIRAM_SUPPORTy CONFIG_SPIRAM_MODE_DIOy CONFIG_ESP32_DEFAULT_CPU_FREQ_240y CONFIG_ESP32_WIFI_PS_NONEy CONFIG_ESP32_CAMERA_FB_COUNT26.2 main.c完整源码#include esp_camera.h #include esp_wifi.h #include esp_event.h #include esp_log.h #include esp_http_server.h #include nvs_flash.h #include freertos/FreeRTOS.h #include freertos/task.h #define WIFI_SSID 你的WiFi名称 #define WIFI_PASS 你的WiFi密码 static const char *TAG CAM_STREAM; static camera_config_t camera_config { .pin_pwdn -1, .pin_reset -1, .pin_xclk 0, .pin_sscb_sda 26, .pin_sscb_scl 27, .pin_d7 35, .pin_d6 34, .pin_d5 39, .pin_d4 36, .pin_d3 21, .pin_d2 19, .pin_d1 18, .pin_d0 5, .pin_vsync 25, .pin_href 23, .pin_pclk 22, .xclk_freq_hz 20000000, .ledc_timer LEDC_TIMER_0, .ledc_channel LEDC_CHANNEL_0, .pixel_format PIXFORMAT_JPEG, .frame_size FRAMESIZE_SVGA, .jpeg_quality 12, .fb_count 2, .fb_location CAMERA_FB_IN_PSRAM, .grab_mode CAMERA_GRAB_WHEN_EMPTY, }; static esp_err_t stream_handler(httpd_req_t *req) { camera_fb_t *fb NULL; esp_err_t res ESP_OK; char part_buf[64]; res httpd_resp_set_type(req, multipart/x-mixed-replace;boundaryframe); if (res ! ESP_OK) return res; while (true) { fb esp_camera_fb_get(); if (!fb) { ESP_LOGE(TAG, Camera capture failed); res ESP_FAIL; break; } size_t hlen snprintf(part_buf, sizeof(part_buf), \r\n--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n, fb-len); res httpd_resp_send_chunk(req, part_buf, hlen); if (res ESP_OK) { res httpd_resp_send_chunk(req, (const char *)fb-buf, fb-len); } if (res ESP_OK) { res httpd_resp_send_chunk(req, \r\n, 2); } esp_camera_fb_return(fb); if (res ! ESP_OK) break; } return res; } static httpd_handle_t start_server(void) { httpd_config_t config HTTPD_DEFAULT_CONFIG(); config.server_port 80; config.ctrl_port 32768; httpd_handle_t server NULL; if (httpd_start(server, config) ! ESP_OK) { ESP_LOGE(TAG, HTTP server start failed); return NULL; } httpd_uri_t stream_uri { .uri /stream, .method HTTP_GET, .handler stream_handler, .user_ctx NULL }; httpd_register_uri_handler(server, stream_uri); return server; } static void wifi_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) { if (base WIFI_EVENT id WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (base WIFI_EVENT id WIFI_EVENT_STA_DISCONNECTED) { esp_wifi_connect(); ESP_LOGI(TAG, Reconnecting to WiFi...); } else if (base IP_EVENT id IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event (ip_event_got_ip_t *)data; ESP_LOGI(TAG, Got IP: IPSTR, IP2STR(event-ip_info.ip)); start_server(); } } static void wifi_init(void) { esp_netif_init(); esp_event_loop_create_default(); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(cfg); esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL); esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL); wifi_config_t wifi_config { .sta { .ssid WIFI_SSID, .password WIFI_PASS, .threshold.authmode WIFI_AUTH_WPA2_PSK, }, }; esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(WIFI_IF_STA, wifi_config); esp_wifi_start(); esp_wifi_set_ps(WIFI_PS_NONE); } void app_main(void) { esp_err_t ret nvs_flash_init(); if (ret ESP_ERR_NVS_NO_FREE_PAGES || ret ESP_ERR_NVS_NEW_VERSION_FOUND) { nvs_flash_erase(); nvs_flash_init(); } if (esp_camera_init(camera_config) ! ESP_OK) { ESP_LOGE(TAG, Camera init failed); return; } ESP_LOGI(TAG, Camera init success); wifi_init(); }6.3 编译烧录与实测数据编译命令idf.py set-target esp32 idf.py build idf.py -p COM3 flash monitor把COM3换成你实际的串口端口。烧录前记得GPIO0接GND烧录后断开GPIO0再按复位。实测数据在2.4GHz WiFi、信道11、距离路由器3米无遮挡的条件下SVGA分辨率、jpeg_quality12、fb_count2的配置下浏览器端看到的帧率稳定在10-13帧每秒单帧大小35-45KB端到端延迟约300ms。把分辨率降到VGA后帧率可以到20帧以上但清晰度下降明显。把jpeg_quality调到20数值越大压缩越狠后单帧降到20KB左右帧率提升到15帧以上但画面细节损失较大。功耗方面WiFi传输状态下整板电流约180mA峰值可以到250mA。如果用电池供电建议选容量2000mAh以上的锂电池续航大约8-10小时。7. 几个值得尝试的优化方向如果你已经跑通了基础版本下面几个方向可以进一步提升效果。第一个是调整XCLK频率。默认20MHz降到10MHz可以减少图像噪点尤其在光线不足的环境下画质提升明显代价是帧率减半。升到24MHz可以提升帧率但部分OV2640模组在24MHz下不稳定需要实测。第二个是启用硬件JPEG编码。ESP32-S2和ESP32-S3内置了JPEG编码器可以分担CPU压力。但ESP32-CAM用的是ESP32-S芯片没有这个硬件模块所以只能靠OV2640内部压缩。如果你用的是ESP32-S3模组可以在menuconfig里启用硬件JPEG编码帧率会有明显提升。第三个是加一个简单的网页前端。ESP32-CAM本身只提供MJPEG流你可以把HTML页面也放在ESP32的HTTP服务器上访问根路径返回一个包含img src/stream的页面这样用户不需要记路径打开IP就能看到画面。第四个是加入运动检测。在传输之前比较相邻两帧的JPEG数据大小差异如果差异小于阈值就跳过传输这样可以大幅降低WiFi带宽占用。实现方式是在stream_handler里维护上一帧的哈希值或数据长度差异不大就不发送。这个优化在监控场景下特别有用静止画面时几乎不占带宽。我在实际使用中发现ESP32-CAM最舒服的用法是固定在某个位置做定时抓拍而不是持续推流。定时抓拍的场景下可以把帧率降到1帧每秒功耗降到50mA以下配合深度睡眠模式一块电池能撑好几天。需要的时候再唤醒传输传完继续睡。这个模式比持续推流实用得多也更适合电池供电的场景。
返回列表