ESP32-CAM 远程监控的实现
1. ESP32-CAM 通过 websocket 发送图片流数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 #include "WiFi.h" #include "esp_camera.h" #include <ArduinoJson .h > #include <WebSocketsClient _Generic.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 #if USE_SSL #define WS_SERVER "ws://xx.xx.xx.xx" #define WS_PORT x #else #define WS_SERVER "xx.xx.xx.xx" #define WS_PORT x #endif const char* hostname = "ESP32CAM" ;const char* ssid = "SONDER" ;const char* password = "sonderswifi" ;WebSocketsClient webSocket;void webSocketEvent (WStype_t type, uint8_t * payload, size_t length ) { switch (type) { case WStype _DISCONNECTED : Serial .printf ("[WSc] Disconnected!\n" ); break ; case WStype _CONNECTED : { Serial .printf ("[WSc] Connected to url: %s\n" , payload); webSocket.sendTXT ("camlogin" ); } break ; case WStype _TEXT : Serial .printf ("[WSc] get text: %s\n" , payload); break ; case WStype _BIN : break ; case WStype _PING : Serial .printf ("[WSc] get ping\n" ); break ; case WStype _PONG : Serial .printf ("[WSc] get pong\n" ); break ; } } 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_QVGA ; config.jpeg_quality = 6 ; 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 setup ( ){ Serial .begin (115200 ); WiFi .begin (ssid, password); while (WiFi .status () != WL_CONNECTED ) { delay (1000 ); Serial .println ("Connecting to WiFi.." ); } Serial .println (WiFi .localIP ()); setupCamera (); #if USE_SSL webSocket.beginSSL ("xx.x.xx.xx" , xx); #else webSocket.begin ("xx.xx.xx.xx" , xx, "/" ); #endif webSocket.onEvent (webSocketEvent); webSocket.setReconnectInterval (5000 ); webSocket.enableHeartbeat (15000 , 3000 , 2 ); } unsigned long messageTimestamp = 0 ; void loop ( ) { webSocket.loop (); uint64_t now = millis (); if (now - messageTimestamp > 10 ) { messageTimestamp = now; camera_fb_t * fb = NULL ; fb = esp_camera_fb_get (); if (!fb) { Serial .println ("Camera capture failed" ); return ; } webSocket.sendBIN (fb->buf,fb->len); Serial .println ("Image sent" ); esp_camera_fb_return (fb); } }
2. websocket 服务端接收图片流,并传输给客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const ws = require ("nodejs-websocket" );let connectionsList = []; const createServer = ( ) => { let server = ws.createServer ((connection ) => { connectionsList.push (connection); connection.on ("text" , function (result ) { console .log ("发送消息" , result); }); connection.on ("binary" , (inStream ) => { console .log ("连接的客户端数量" , server.connections .length ); if (server.connections .length == 1 ) return ; const client = server.connections [server.connections .length - 1 ]; inStream.on ("readable" , () => { let chunk; while (null !== (chunk = inStream.read ())) { client.send (chunk, { binary : true }); } }); inStream.on ("end" , () => { }); }); connection.on ("connect" , function (code ) { console .log ("开启连接" , code); }); connection.on ("close" , function (code ) { console .log ("关闭连接" , code); }); connection.on ("error" , function (code ) { console .log ("异常关闭" , code); }); }); return server; }; createServer ().listen (1332 );
3. 客户端连接 websocket 实时展示图像信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 var ws = new WebSocket ("ws://xx.xx.xx.xx:xx" );ws.onopen = function ( ) { document .getElementById ("res" ).innerHTML = "当前客户端已经连接到websocket服务器" ; const idMsg = { command : "set-id" , data : { id : "123456" , }, }; ws.send (JSON .stringify (idMsg)); }; ws.onmessage = function (evt ) { const blob = new Blob ([evt.data ], { type : "image/jpeg" , }); const url = URL .createObjectURL (blob); const img = document .createElement ("img" ); img.src = url; $("#img" ).attr ("src" , url); }; ws.onclose = function ( ) { alert ("连接已关闭..." ); };
最后:
将第一步中的代码通过 Arduino 烧录进开发板,然后连接 wifi,就可以在串口中监听到信息输出了。
然后在服务中打印查看传输的数据,比如 buffer 的长度,注意:我这里的服务端代码仅实现了功能,还有优化的地方,我这里就懒的做了。
最后在客户端打开 html,将 js 文件引入进去,一切正常应该就能看到 ESP32-CAM 的实时图像信息了。
更新:
ESP32 相机模块的引脚定义。在使用 ESP32 来连接和控制相机模块时,需要指定相应的引脚来进行通信和数据传输。
以下是每个引脚定义的含义:
PWDN_GPIO_NUM:电源使能引脚,用于控制相机模块的电源,GPIO 32。
RESET_GPIO_NUM:复位引脚,用于复位相机模块,-1 表示没有对应的引脚。
XCLK_GPIO_NUM:像素时钟引脚,用于同步像素数据传输的时钟信号,GPIO 0。
SIOD_GPIO_NUM:串行数据引脚,GPIO 26。
SIOC_GPIO_NUM:串行时钟引脚,GPIO 27。
Y9_GPIO_NUM 到 Y2_GPIO_NUM:数据引脚,用于接收图像像素数据,分别对应 GPIO 35 到 GPIO 5。
VSYNC_GPIO_NUM:垂直同步引脚,用于标识图像帧的垂直同步位置,GPIO 25。
HREF_GPIO_NUM:行同步引脚,用于标识图像帧的行同步位置,GPIO 23。
PCLK_GPIO_NUM:像素时钟引脚,用于同步像素数据传输的时钟信号,GPIO 22。
这些引脚定义可以根据具体的硬件连接和相机模块的规格进行调整。修改这些定义可以确保正确地与相机进行通信,并在 ESP32 上实现图像捕获和处理等功能。
ledc_channel 和 ledc_timer:用于配置 LEDC(LED 控制器)的通道和定时器。在相机模块中,LEDC 可以用于控制 LED 灯的亮度,这里选择了 LEDC_CHANNEL_0 和 LEDC_TIMER_0。
pin_d0 到 pin_d7:用于配置数据引脚 D0 到 D7。这些引脚与之前定义的 Y2 到 Y9 相对应。
pin_xclk、pin_pclk、pin_vsync 和 pin_href:用于配置时钟和同步引脚。分别是像素时钟引脚 XCLK、像素时钟引脚 PCLK、垂直同步引脚 VSYNC 和行同步引脚 HREF。
pin_sscb_sda 和 pin_sscb_scl:用于配置串行数据传输引脚 SDA 和 SCL。
pin_pwdn 和 pin_reset:用于配置电源使能引脚和复位引脚。
xclk_freq_hz:设置像素时钟频率,这里是 20,000,000 Hz(20 MHz)。
pixel_format:设置像素格式,这里选择的是 JPEG 格式。
frame_size:设置帧的大小,这里设置为 QVGA(320x240)。
jpeg_quality:设置 JPEG 压缩质量,范围是 0-63,这里选取了 6。
fb_count:帧缓冲区的数量,这里设置为 1。