这次带来一个好玩的 ESP32 项目:虚拟摄像头,就是将ESP32 S3 的板子烧录之后,系统中会出现一个USB摄像头,打开Camera后能够看到播放出来的视频。
下面介绍具体的实现方式。
目前 Arduino ESP32 尚不支持 USB Camera,因此,这次的项目是基于IDF 来完成的。特别注意:对于硬件有如下要求:
1.必须是 ESP32 S2或者 S3,其他型号的ESP32 目前不支持原生USB编程,所以只能使用 S2 或者 S3;
2.必须带有 PSRAM,因为这个项目是根据Demo 修改而来,Demo 要求带有 PSRAM。我对编译环境不熟悉,这部分没有修改, 理论上移除对于 Camera 的支持即可在没有 PSRAM 的板子上使用;
3.必须是 16MB 的 ESP32 模块,如果想在更小容量的板子上使用,可以删除项目中的JPEG素材缩减体积,同时修改项目配置为 4MB 或者8MB.
如果你对ESP32 IDF环境比较熟悉,可以修改去掉上面提到的2的限制;同样的,可以删除部分图片使得4MB的ESP32 也可以支持。如果你无法做到这两点,可以像我一样使用 ESP32 S3 EYS 兼容版。
先介绍一下如何使用我的代码:
安装 ESP32 IDF 编译环境
2.下载安装 esp-iot-solution,解压后放在c: 根目录下
3.尝试编译C:\esp-iot-solution\examples\usb\device\usb_webcam 确保编译环境无误
4.基本的命令有
a. 编译命令 idf.py build (特别注意编译时需要联网)
b.烧录 idf.py -p COM端口 flash
c.串口监视器 idf.py -p COM端口 monitor
d.上述指令可以放在一起,例如:
idf.py -p com6 build flash monitor
e.监视器可以使用 ctrl+] 退出
f.项目配置 idf.py menuconfig ()
5.将usb_webcam1 解压到C:\esp-iot-solution\examples\usb\device目录下
使用 idf.py -p com6 build flash monitor 编译后会自动烧录然后打开串口监视器。
6.打开系统自带的相机程序,切换到ESP32 摄像头即可看到播放内容
上面介绍了如何直接使用代码,接下来介绍一下项目基本实现原理。
- esp-iot-solution 提供了一个USB摄像头的例子。它将自己报告为一个USB相投设备,从板载的摄像头读取数据,然后从USB端口输出;
- 我代码的修改是在上报数据中使用SPIFFS存放的数进行替换了摄像头的数据
- 下面介绍一下 SPIFFS中存放的内容是如何制作的
- 使用 Easy2Convert GIF to JPG 工具将GIF 每一帧转化为 JPG 格式
5. 使用 XnView 处理上面的 JPG 文件。需要将所有的图片名为为 0000、0001…..0XXX 这种名称;同样使用这个软件将所有的图片都修改为 320*240 大小。
修改的代码主要部分在动作就是按照孙旭检查 SPIFFS 中,storage 下面是否有XXXX.jpg 这样的文件,如果有就读取出来作为摄像头数据上报,如果XXXX.JPG 不存在,那么就说明读取完毕,再从 0000开始。
static uvc_fb_t* camera_fb_get_cb(void *cb_ctx)
{
s_fb.uvc_fb.timestamp.tv_usec++;
char buffer[64];
struct stat file_stat;
int filesize;
FILE *fd = NULL;
sprintf(buffer,"/storage/%04d.jpg",PicIndex);
ESP_LOGI(TAG, "p1 %s %d",buffer,PicIndex);
if (stat(buffer, &file_stat) == -1) {
PicIndex=0;
ESP_LOGI(TAG, "ZivHer2");
sprintf(buffer,"/storage/%04d.jpg",PicIndex);
} else {PicIndex++;}
fd = fopen(buffer, "rb");
ESP_LOGI(TAG, "ZivHer3");
fseek(fd, 0, SEEK_END);
filesize = ftell(fd);
rewind(fd);
ESP_LOGI(TAG, "send %d",filesize);
fread(&PicBuffer, 1, filesize, fd);
s_fb.uvc_fb.buf = PicBuffer;
s_fb.uvc_fb.len=filesize;
fclose(fd);
vTaskDelay(pdMS_TO_TICKS(100));
return &s_fb.uvc_fb;
}