本方案为esp32cam 服务端 浏览器 三端联合使用。将服务端部署在公网即可远程使用,没有远程需求,可以直接在局域网使用。代码无需修改。
本文取缔了esp32cam自身运行http服务的相关逻辑,使得esp32cam只负责不停拍照片发给服务端,从而减少esp32cam的压力,提升了其视频流畅度。
本文代码开源地址:https://gitcode.net/qq_26700087/simpleVideoServer,该项目的比当前文章更新,更加流畅,并且支持合宙ESP32S3。如果使用本文的代码,则使用本文提供链接中的发行版文件。使用该项目最新代码,请使用该项目的页面提供的下载服务端链接下载服务端发行版
一种较为流畅的esp32cam远程视频方案
| 物料 | 说明 |
|---|---|
| esp32cam开发板 | esp32cam,本人是使用其自带摄像头,代码仅仅在其自带摄像头下测试 |
| 一台装有windows或者linux操作系统的计算机 | 用于运行服务端 |
| USB转TTL模块/或底座 | 用于烧录/串口监控 |
| 杜邦线若干 | 用于io0接地/使用底座烧录不需要 |
鄙人无MacOs系统PC,未对MacOS进行测试。但服务端由java编写,自行下载macOS版本的jdk17,运行服务端,理论上不会有问题
也可以将java源代码重新编译,甚至移植到安卓App上。
本文以windows系统为例,使用安信可家esp32cam模块 和安信可家USB转TTL CP2012模块。
本文以ardunio 框架开发,你可以选择 ardunio ide 编译和烧录
或者platformio(基于vs code/clion)新建项目选择 以ardunio framework新建。
打开菜单 -> 文件 -> 首选项

在附加开发板管理器网址中追加一行
https://dl.espressif.com/dl/package_esp32_index.json
最近出现了下面这个地址,这个是esp32 的2.0环境,一些新特性需要,可能不需要上方的那个json。
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
在 工具 -> 开发板 -> 开发板管理器里 搜索 esp32 点击进行安装。
此处可能需要把dns修改为腾讯或者阿里公共dns,才容易成功。

选择esp32cam

新建项目找到 AI Thinker ESP32-CAM,则选择了开发板。框架选择 ardunio。若没有初始化esp32环境,会自动下载,同样建议修改dns。
新建后生成的ini文件如下(串口相关设置需要手动添加)
[env:esp32cam]
platform = espressif32
board = esp32cam
framework = arduino
upload_speed= 115200
upload_port = COM3
monitor_speed= 115200
monitor_port = COM3
存在多个文件和服务端相关代码,点此跳转
引导方面本文更加详细。可先参考本文。
esp32cam代码共两个文件。也可以合成一个。
表示esp32cam相关GPIO引脚定义
#ifndef AI_THINKER_32_CAM_META
#define AI_THINKER_32_CAM_META
#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
#endif
在ardunio ide里无需考虑该文件名,而只是把该文件内容复制到你的.ino 文件中。
请按照具体情况修改wifi的ssid和密码,一般可以使用你pc运行服务端,可以将PC的ip填写到以下源文件host变量的值中。
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#define CAMERA_MODEL_AI_THINKER
#include "ai_thinker_esp32_cam_meta.h"
char* ssid = "test0";
const char* passwd = "12345687";
const char* host = "192.168.137.1";
WiFiClient streamSender;
void connectWifi(const char* ssid, const char* passphrase) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
Serial.println("connecting to router... ");
//等待wifi连接成功
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.print("\nWiFi connected, local IP address:");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
while (!Serial) {
/* code */
}
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;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
Serial.println("get sensor ");
sensor_t* s = esp_camera_sensor_get();
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_VGA);
connectWifi(ssid, passwd);
Serial.println("connect stream channel");
if (!streamSender.connect(host, 8004)) {
Serial.println("connect stream channel failed");
}
streamSender.setNoDelay(true);
// 发送mac地址作为设备序列号,用于摄像头频道号
uint8_t mac[6];
WiFi.macAddress(mac);
char macStr[12] = {0};
sprintf(macStr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3],
mac[4], mac[5]);
Serial.println("sprint mac ");
streamSender.print(String(macStr));
streamSender.flush();
}
void loop() {
camera_fb_t* fb = NULL;
size_t len = 0;
Serial.println("do loop");
uint8_t end[5] = {'j', 'p', 'e', 'g', '\n'};
while (true) {
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return;
}
len = fb->len;
streamSender.write(fb->buf, len);
streamSender.write(end, 5);
streamSender.flush();
esp_camera_fb_return(fb);
}
}
查看PC的ip的办法
按win + R 输入cmd 按enter打开控制台
输入 ipconfig 回车,找到有ipv4地址的那一行。如果有多个适配器,大概率需要自己确定和无线路由器所在同一网络的ip。
一般wifi连接则是网卡名带 无线适配器,而路由器网线直连则自己抉择。
| USB转TTL模块引脚 | esp32cam引脚 |
|---|---|
| 5V | 5V |
| GND | GND |
| TXD | U0R |
| RXD | U0T |
当上载控制台出现连接某某com口,请尽快连接IO0和GND,再按RST。
使用烧录底座将esp32cam安上底座,则无需考虑针脚连接具体如何。
鄙人不知烧录底座是否可以串口监控,使用USB转TTL模块时,在烧录完成后,断开IO0与GND的连接,再按RST重启esp32cam则可以查看串口打印,但此时没有运行服务端SimpleVideoServer。
根据自己需要选择以下任意方式运行服务端
解压之后,进入对应目录点击run.bat文件启动服务器。
需要unzip或p7zip等可以解压zip的应用
运行
unzip SimpleVideoSever_linux.zip
cd linux_release/
sh run.sh
发行版内部仅仅是一些java17 版本的class文件和jre以及启动脚本,你可以使用任意其它的jre17运行这些class文件。并非需要发行版。
访问视频服务
新增了频道功能,也就是说每个摄像头处在不同频道,访问不同的摄像头需要不同的地址。
如鄙人执行的服务端日志打印含有esp32Cam接入的后的相关打印如下:
D:\Users\immor\idea\SimpleVideoServer\out\win_release>.\jre\bin\java -classpath SimpleVideoServer org.btik.server.video.Main
bio video server started
bio Device Channel started
new channel:
http://127.0.0.1:8003/video/441793EE3C08
http://192.168.0.116:8003/video/441793EE3C08
http://192.168.137.1:8003/video/441793EE3C08
start /192.168.137.234:53051
每接入一个esp32Cam会新建一个频道,在new channel:的打印后会出现,相关可以访问视频流的地址。
你可以在本机,或者局域网的其他设备访问。
部署在云服务器的同学把端口打开后,把内网ip替换成公网ip,或者域名即可。
关于如何把视频节目嵌入其它网页
如果你擅长web开发,或者不喜欢在多个窗口查看多个摄像头可以参考以下方法增加自己的内容。
本视频http请求是允许跨域的,若希望在自己的网页里面加入本服务端提供esp32Cam视频窗口,
其实不用html的 iframe标签,img标签即可。
比如以下html代码,新建一个文件比如a.html 复制以下内容,根据实际情况,替换img标签src属性的内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>直播间</title>
<style>
.videoContainer{
display: inline-block;
}
</style>
</head>
<body>
<div style="padding: 0;margin:30px auto; width: 1300px">
<div class="videoContainer">
<img src="http://127.0.0.1:8003/video/441793EE3C08">
</div>
<div class="videoContainer">
<img src="http://127.0.0.1:8003/video/58BF2581F024">
</div>
</div>
</body>
</html>
以上是我的两个esp32Cam的视野,效果如下:

也就是说,你可以把该项目植入任何其它可以用到web前端的项目。通过查询在线设备,可以动态打开每个摄像头的视频。
一般无需修改,但提供改法。后续此处可能会有变化,可以跟随该开源项目SimpleVideoSever具体描述。
light-video.properties
里面含有三个配置
http.port=8003
http.clients.limit=10
stream.port=8004
http.port 为http的端口。
http.clients.limit 摄像头在线接入限制数。本意是想限制客户端参与的数量故名为http.clients.limit,实际的实现是限制了摄像头的数量
stream.port esp32cam像服务端发送照片的端口,在本项目中使用该默认端口,如果需要修改,一并修改esp32cam代码中连接的端口。一般无需修改。
使用远程服务器如云服务器同学则无需额外指导。注意本服务端毫不安全,没有任何安全机制,在公网使用不宜长久暴露服务端口。
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p