文档类型:早期 Korvo S3 网页 MJPEG 图传笔记。
请优先阅读:Korvo camera_webserver 例程详解 · 通用 API:HTTP、Camera
1. 项目概述
本教程将介绍如何使用ESP32-S3开发板实现网页图传功能。通过这个项目,您可以将摄像头捕获的图像实时传输到网页上,实现远程监控的效果。
2. 硬件准备
- ESP32-S3开发板(本项目使用的是酷世DIY Korvo开发板)
- 兼容的摄像头模块
- 电源线和数据线
- 稳定的WiFi网络
3. 开发环境配置
3.1 必要的软件
- ESP-IDF(乐鑫物联网开发框架)
- 编译工具链
3.2 项目依赖
项目使用了ESP32-Camera组件,在idf_component.yml中已经配置:
dependencies:
espressif/esp32-camera: "^2.0.13"
idf: ">=4.4"
3.3 PSRAM配置
由于图像处理需要较大内存,必须启用PSRAM。在sdkconfig.defaults中已经配置:
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
4. 代码详解
4.1 摄像头初始化
摄像头初始化是整个项目的基础,我们需要正确配置摄像头的引脚和参数:
static camera_config_t camera_config = {
.pin_pwdn = CAM_PIN_PWDN, // 电源引脚(本例中未使用)
.pin_reset = CAM_PIN_RESET, // 复位引脚(本例中使用软件复位)
.pin_xclk = CAM_PIN_XCLK, // 时钟引脚
.pin_sccb_sda = CAM_PIN_SIOD, // I2C数据线
.pin_sccb_scl = CAM_PIN_SIOC, // I2C时钟线
// 数据引脚D0-D7
.pin_d7 = CAM_PIN_D7,
.pin_d6 = CAM_PIN_D6,
.pin_d5 = CAM_PIN_D5,
.pin_d4 = CAM_PIN_D4,
.pin_d3 = CAM_PIN_D3,
.pin_d2 = CAM_PIN_D2,
.pin_d1 = CAM_PIN_D1,
.pin_d0 = CAM_PIN_D0,
// 同步信号引脚
.pin_vsync = CAM_PIN_VSYNC,
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
// 时钟配置
.xclk_freq_hz = 20000000, // 20MHz时钟频率
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
// 图像格式配置
.pixel_format = PIXFORMAT_JPEG, // 使用JPEG格式(传输效率高)
.frame_size = FRAMESIZE_VGA, // 分辨率设置为VGA (640x480)
// 图像质量和缓冲区配置
.jpeg_quality = 12, // JPEG质量(0-63,数值越小质量越高)
.fb_count = 2, // 帧缓冲区数量,大于1时工作在连续模式
.fb_location = CAMERA_FB_IN_PSRAM, // 帧缓冲区存放在PSRAM中
.grab_mode = CAMERA_GRAB_WHEN_EMPTY, // 抓取模式
};
初始化函数:
static esp_err_t init_camera(void)
{
// 初始化摄像头
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Camera Init Failed");
return err;
}
return ESP_OK;
}
4.2 WiFi连接
WiFi连接是实现网页图传的前提,我们使用app_wifi.c中的函数来连接WiFi:
void wifi_init_sta(void)
{
// 创建WiFi事件组
s_wifi_event_group = xEventGroupCreate();
// 初始化网络接口
ESP_ERROR_CHECK(esp_netif_init());
// 创建默认事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
// 初始化WiFi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 注册事件处理函数
// ... 事件注册代码 ...
// 配置WiFi参数
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID, // WiFi名称
.password = EXAMPLE_ESP_WIFI_PASS, // WiFi密码
// ... 其他配置 ...
},
};
// 设置WiFi模式并启动
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
// 等待连接成功或失败
// ... 等待代码 ...
}
4.3 HTTP服务器和图像流处理
HTTP服务器负责接收客户端请求并返回图像流:
// 定义HTTP流的边界和内容类型
#define PART_BOUNDARY "123456789000000000000987654321"
static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
// HTTP GET处理函数
esp_err_t http_get_handler(httpd_req_t *req)
{
// 打印可用内存
printf("esp_get_free_heap_size : %d \n", esp_get_free_heap_size());
camera_fb_t *fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len;
uint8_t *_jpg_buf;
char *part_buf[64];
static int64_t last_frame = 0;
// 设置响应类型为多部分内容
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if (res != ESP_OK)
{
return res;
}
// 循环发送图像流
while (true)
{
// 获取摄像头图像
fb = esp_camera_fb_get();
if (!fb)
{
ESP_LOGE(TAG, "Camera capture failed");
res = ESP_FAIL;
break;
}
// 如果不是JPEG格式,转换为JPEG
if (fb->format != PIXFORMAT_JPEG)
{
bool jpeg_converted = frame2jpg(fb, 50, &_jpg_buf, &_jpg_buf_len);
if (!jpeg_converted)
{
ESP_LOGE(TAG, "JPEG compression failed");
esp_camera_fb_return(fb);
res = ESP_FAIL;
}
}
else
{
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
// 发送图像数据
if (res == ESP_OK)
{
// 发送边界
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if (res == ESP_OK)
{
// 发送图像头信息(包含长度)
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if (res == ESP_OK)
{
// 发送图像数据
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
// 释放资源
if (fb->format != PIXFORMAT_JPEG)
{
free(_jpg_buf);
}
esp_camera_fb_return(fb);
// 计算帧率
int64_t fr_end = esp_timer_get_time();
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)",
(uint32_t)(_jpg_buf_len / 1024),
(uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
}
return res;
}
4.4 HTTP服务器任务
创建HTTP服务器任务:
static void http_test_task(void *pvParameters)
{
ESP_LOGI(TAG, "Http Start");
// 创建HTTP服务器
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
// 启动HTTP服务器
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
if (httpd_start(&server, &config) == ESP_OK)
{
// 注册URI处理函数
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &stream);
}
else
{
ESP_LOGI(TAG, "Error starting server!");
}
ESP_LOGI(TAG, "Http End");
vTaskDelete(NULL);
}
4.5 主函数
主函数负责初始化各个模块并启动HTTP服务器任务:
void app_main(void)
{
// 初始化NVS(非易失性存储)
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 初始化摄像头
if (ESP_OK != init_camera())
{
return;
}
// 测试摄像头是否正常工作
camera_fb_t *fb = esp_camera_fb_get();
if (!fb)
{
ESP_LOGE(TAG, "Camera capture failed");
}
else
ESP_LOGI(TAG, "Camera capture ok!!!!!!");
// 初始化WiFi
wifi_init_sta();
// 创建HTTP服务器任务
xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL);
}
5. 如何使用
5.1 编译和烧录
- 确保已经安装ESP-IDF开发环境
- 在项目目录下执行以下命令:
idf.py build idf.py -p [COM端口] flash
5.2 查看串口输出
烧录完成后,打开串口监视器查看输出信息:
idf.py -p [COM端口] monitor
5.3 访问网页图传
- 在串口输出中找到ESP32-S3的IP地址
- 在浏览器中输入:
http://[ESP32-S3的IP地址]/stream - 浏览器将显示摄像头实时图像
6. 工作原理
6.1 MJPEG流原理
本项目使用MJPEG(Motion JPEG)流技术实现图像传输。MJPEG是一种视频格式,它将视频编码为一系列JPEG图像。在HTTP传输中,使用multipart/x-mixed-replace内容类型,让浏览器不断替换显示的图像,从而实现视频效果。
每个JPEG图像的HTTP响应格式如下:
--[边界标识]
Content-Type: image/jpeg
Content-Length: [图像大小]
[JPEG图像数据]
--[边界标识]
...
6.2 数据流程
- 摄像头捕获图像 → 存储在PSRAM中
- HTTP服务器接收客户端请求
- 服务器从摄像头获取图像
- 服务器将图像转换为JPEG格式(如果需要)
- 服务器通过HTTP流发送图像
- 浏览器接收并显示图像
- 重复步骤3-6,形成连续的视频流
7. 常见问题与解决方案
7.1 内存不足
症状:程序崩溃或无法启动 解决方案:
- 确保已启用PSRAM
- 降低图像分辨率
- 减少帧缓冲区数量
7.2 图像质量问题
症状:图像模糊或有噪点 解决方案:
- 调整JPEG质量参数(降低
jpeg_quality值) - 更换更好的摄像头模块
- 确保光线充足
7.3 连接问题
症状:无法连接到WiFi或网页无法访问 解决方案:
- 检查WiFi名称和密码是否正确
- 确保ESP32-S3和客户端在同一网络
- 检查防火墙设置
8. 扩展功能建议
- 添加网页控制界面,可以调整摄像头参数
- 实现图像保存功能
- 添加移动侦测功能
- 集成人脸识别功能
- 添加多客户端支持
9. 总结
通过本教程,您已经学会了如何使用ESP32-S3和摄像头模块实现网页图传功能。这个项目可以作为远程监控、智能家居、机器人视觉等应用的基础。希望这个教程对您有所帮助!