这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » [ESP-IDF]使用ESP-32-Xiao-Sense开发板进行MJPEG通过

共1条 1/1 1 跳转至

[ESP-IDF]使用ESP-32-Xiao-Sense开发板进行MJPEG通过HTTP进行推流播放

工程师
2026-02-21 22:30:48   被打赏 50 分(兑奖)     打赏

简介

最近搞了块Seeed Studio的XIAO ESP32-S3 Sense开发板,这小东西麻雀虽小五脏俱全——自带摄像头、麦克风、SD卡槽,还支持WiFi。今天就来折腾一下,用ESP-IDF原生框架实现MJPEG视频流通过HTTP推流,让浏览器能直接看到实时画面。

为什么选择ESP-IDF?

之前玩ESP32系列的时候,用Arduino框架居多,简单上手快。但这次想深入一点,直接用乐鑫官方的ESP-IDF框架开发。原因有三:

1-性能更好:ESP-IDF底层优化更到位,对多任务、内存管理更精细

2-功能更全:很多底层API只有ESP-IDF才有

3-生产可用:如果想做产品,ESP-IDF是更靠谱的选择

其实最重要的原因是因为:虽然这个开发板带有WebCam的demo(Arduino环境)但是仅仅具有拍照的功能,使用Arduino开发的话并不知道怎么通过MJPEG编码进行推流播放。而ESP-IDF具有成熟的摄像头驱动库 ESP- Camera也可以加速我们的开发。

根据官方原理图,摄像头和音频的引脚分配如下:

// 摄像头引脚
#define XCLK_GPIO_NUM   10
#define SIOD_GPIO_NUM   40
#define SIOC_GPIO_NUM   39
#define Y9_GPIO_NUM     48
#define Y8_GPIO_NUM     11
#define Y7_GPIO_NUM     12
#define Y6_GPIO_NUM     14
#define Y5_GPIO_NUM     16
#define Y4_GPIO_NUM     18
#define Y3_GPIO_NUM     17
#define Y2_GPIO_NUM     15
#define VSYNC_GPIO_NUM  38
#define HREF_GPIO_NUM   47
#define PCLK_GPIO_NUM   13

// 麦克风引脚
#define I2S_SCK_GPIO    42  // BCLK
#define I2S_DIN_GPIO    41  // DATA

整个项目的核心思路很简单:

1-初始化WiFi,工作在AP模式(也可以改成STA模式连接现有WiFi,不建议、因为延迟较大)

2-初始化摄像头,配置为JPEG输出

3-初始化I2S音频(这次先不用,没有研究明白怎么一起推流出去)

4-启动HTTP服务器,提供两个端点:/:返回HTML页面,显示视频流 。 /stream:MJPEG视频流,用multipart/x-mixed-replace格式


具体实现步骤如下

一、首先初始化项目,在组件管理器中增加ESP-Camera的支持

## IDF Component Manager Manifest File
dependencies:
## Required IDF version
idf:
version: '>=4.1.0'
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"
# # For 3rd party components:
# username/component: ">=1.0.0,<2.0.0"
espressif/esp32-camera: ^2.1.5


MJPEG流原理

MJPEG其实就是一帧一帧的JPEG图片连续传输,用特殊的boundary分隔。浏览器收到后会自动连续播放,实现视频效果。代码里用的是AP模式,开发板自己创建一个WiFi热点,手机或电脑直接连上来访问。这样最省事,不用配置路由器而且延迟还低。

static void wifi_init()
{
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_ap();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = "99999999999",
            .password = "88888888",
            .ssid_len = 11,
            .channel = 6,
            .authmode = WIFI_AUTH_WPA2_PSK,
            .max_connection = 4,
        },
    };

    esp_wifi_set_mode(WIFI_MODE_AP);
    esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
    esp_wifi_set_ps(WIFI_PS_NONE); // 禁用省电模式保证流畅度
    esp_wifi_start();
    
    ESP_LOGI(TAG, "WiFi AP started. SSID: %s", WIFI_SSID);
}


二、 摄像头初始化

XIAO Sense用的是OV2640传感器(最新版和我现在用的版本是OV3660),最高支持UXGA(1600x1200)。但视频流用不了那么高,我选SVGA(800x600)平衡画质和流畅度。

static void camera_init()
{
    camera_config_t config = {
        .xclk_freq_hz = 20000000,
        .pixel_format = PIXFORMAT_JPEG,
        .frame_size = FRAMESIZE_SVGA,  // 800x600
        .jpeg_quality = 15,            // 质量越低,帧率越高
        .fb_count = 4,                  // 4个帧缓冲区,平滑采集
        .grab_mode = CAMERA_GRAB_LATEST, // 只取最新帧
        // ... 引脚配置省略
    };

    esp_err_t err = esp_camera_init(&config);
}


三、I2S音频初始化(预留功能)

XIAO Sense板载的MSM261麦克风是I2S接口的数字麦克风。虽然这次没用到音频,但先初始化好,方便后续扩展。

static void audio_init()
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &i2s_chan_handle));

    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, 1),
        .gpio_cfg = {
            .bclk = I2S_SCK_GPIO,  // 42
            .din = I2S_DIN_GPIO,    // 41
            // ws引脚留空,数字麦克风不需要
        },
    };

    i2s_channel_init_std_mode(i2s_chan_handle, &std_cfg);
    i2s_channel_enable(i2s_chan_handle);
}


四、HTTP服务器和MJPEG流处理

这是核心部分。使用ESP-IDF的esp_http_server组件,比Arduino的WebServer更底层,也更灵活。

static esp_err_t http_handler(httpd_req_t *req)
{
    if (strcmp(req->uri, "/") == 0) {
        // 返回HTML页面
        httpd_resp_set_type(req, "text/html; charset=utf-8");
        return httpd_resp_send(req, HTML_PAGE, strlen(HTML_PAGE));
    }
    
    if (strcmp(req->uri, "/stream") == 0) {
        // 设置MJPEG流响应头
        httpd_resp_set_type(req, "multipart/x-mixed-replace; boundary=frame");
        httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
        
        while (1) {
            camera_fb_t *fb = esp_camera_fb_get();
            if (!fb) {
                vTaskDelay(pdMS_TO_TICKS(50));
                continue;
            }
            
            // 发送boundary
            char buf[64];
            size_t hlen = snprintf(buf, sizeof(buf),
                "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n",
                fb->len);
            
            if (httpd_resp_send_chunk(req, buf, hlen) != ESP_OK) break;
            if (httpd_resp_send_chunk(req, (char*)fb->buf, fb->len) != ESP_OK) break;
            if (httpd_resp_send_chunk(req, "\r\n", 2) != ESP_OK) break;
            
            esp_camera_fb_return(fb);
            vTaskDelay(pdMS_TO_TICKS(50)); // 限制约20fps
        }
    }
    
    return ESP_OK;
}

五、前端页面

前端页面是借助AI生成的,因为原本的前端页面比较简洁,使用AI增加了对移动端的适配。代码较多这里就不贴了。见附件即可


效果如下

首先连接WIFI


Weixin Image_20260221222911_358_53.jpg

然后访问对应的IP地址

8d220516845f197d072dd91944b9cf92.jpg

实际上帧率还可以,但是有一定的延迟。目前是采用的直连的方式。不是很建议使用路由器、因为使用路由器的话还需要路由器进行中转一次、帧率会更低。


附件如下

esp32-cam.zip

切勿用于违法犯罪!




关键词: ESP-IDF     ESP-32-Xiao-Sense    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]