ESP32-Cam关闭摄像头流时崩溃问题求助
解决ESP32-Cam关闭浏览器后LoadProhibited崩溃问题
问题根源
关闭浏览器标签页后,TCP连接已断开,但代码中的connected标志仍为true,camLoop持续调用liveCam尝试向失效的WiFiClient实例写入数据,触发非法内存访问(LoadProhibited错误)。此外,直接赋值live_client = client会导致浅拷贝引用,原客户端对象销毁后live_client变为悬空引用,进一步加剧内存访问问题。
修复方案
1. 实时检测客户端连接状态
在liveCam函数开头添加连接检查,若客户端断开则重置状态并终止数据发送:
void liveCam(WiFiClient &client){ // 先检查客户端是否仍连接 if(!client.connected()){ connected = false; client.stop(); return; } // 原有的帧捕获和发送逻辑... }
2. 处理新连接时关闭旧连接
当有新的/video请求到来时,先关闭之前可能存在的旧连接,避免资源冲突:
else if (req == "/video") { // 关闭旧连接 if(connected){ live_client.stop(); connected = false; } live_client = client; live_client.print("HTTP/1.1 200 OK\n"); live_client.print("Content-Type: multipart/x-mixed-replace; boundary=frame\n\n"); live_client.flush(); connected = true; }
3. 优化循环逻辑避免无效调用
在camLoop中,除了检查connected,也可以额外检查live_client.connected(),双重保障:
void camLoop() { http_resp(); if(connected && live_client.connected()){ liveCam(live_client); } else if(connected){ // 客户端已断开但标志未更新,手动重置 connected = false; live_client.stop(); } }
修改后的完整cam.ino代码
#include "esp_camera.h" WiFiServer camServer(81); bool connected = false; WiFiClient live_client; String index_html = "<meta charset=\"utf-8\"/>" \ "<style>" \ "#content {" \ "display: flex;" \ "flex-direction: column;" \ "justify-content: center;" \ "align-items: center;" \ "text-align: center;" \ "min-height: 100vh;}" \ "</style>" \ "<body bgcolor=\"#000000\"><div id=\"content\"><h2 style=\"color:#ffffff\">HTTP ESP32 Cam live stream </h2><img src=\"video\"></div></body>"; void configCamera(){ 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_UXGA; config.jpeg_quality = 10; config.fb_count = 2; esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } } //continue sending camera frame void liveCam(WiFiClient &client){ // 检查客户端连接状态 if(!client.connected()){ connected = false; client.stop(); return; } //capture a frame camera_fb_t * fb = esp_camera_fb_get(); if (!fb) { Serial.println("Frame buffer could not be acquired"); return; } client.print("--frame\n"); client.print("Content-Type: image/jpeg\n\n"); client.flush(); client.write(fb->buf, fb->len); client.flush(); client.print("\n"); //return the frame buffer back to be reused esp_camera_fb_return(fb); } void http_resp(){ WiFiClient client = camServer.available(); /* check client is connected */ if (client.connected()) { /* client send request? */ /* request end with '\r' -> this is HTTP protocol format */ String req = ""; while(client.available()){ req += (char)client.read(); } Serial.println("request " + req); /* First line of HTTP request is "GET / HTTP/1.1" here "GET /" is a request to get the first page at root "/" "HTTP/1.1" is HTTP version 1.1 */ /* now we parse the request to see which page the client want */ int addr_start = req.indexOf("GET") + strlen("GET"); int addr_end = req.indexOf("HTTP", addr_start); if (addr_start == -1 || addr_end == -1) { Serial.println("Invalid request " + req); return; } req = req.substring(addr_start, addr_end); req.trim(); Serial.println("Request: " + req); client.flush(); String s; /* if request is "/" then client request the first page at root "/" -> we process this by return "Hello world"*/ if (req == "/") { s = "HTTP/1.1 200 OK\n"; s += "Content-Type: text/html\n\n"; s += index_html; s += "\n"; client.print(s); client.stop(); } else if (req == "/video") { // 关闭旧连接 if(connected){ live_client.stop(); connected = false; } live_client = client; live_client.print("HTTP/1.1 200 OK\n"); live_client.print("Content-Type: multipart/x-mixed-replace; boundary=frame\n\n"); live_client.flush(); connected = true; } else { /* if we can not find the page that client request then we return 404 File not found */ s = "HTTP/1.1 404 Not Found\n\n"; client.print(s); client.stop(); } } } void startCamera() { camServer.begin(); configCamera(); } void camLoop() { http_resp(); if(connected && live_client.connected()){ liveCam(live_client); } else if(connected){ connected = false; live_client.stop(); } }
原理说明
- 连接状态检测:每次发送帧前确认客户端连接有效性,避免向失效的TCP连接写入数据。
- 旧连接清理:新连接到来时关闭旧连接,防止多个连接占用资源,同时避免悬空引用。
- 双重状态校验:在主循环中额外校验连接状态,确保
connected标志与实际连接状态同步。
内容的提问来源于stack exchange,提问作者PhoenixDev
相关产品推荐
相关产品推荐

