实验室留的,淘宝差不多反正
然后新建一个工程,依赖于hello_world。
idf.py add-dependency "espressif/esp32-camera^2.0.15"
需要启动psram
并且这个板子flash只有2MB
把SPI 改为80MHz,psram和Serial flasher config 都改。
然后add dependency后选则下,基本不变
修改下代码
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
//----------------------------------------------------------------------
#include "esp_log.h"
#include "esp_camera.h"
#define CAM_PIN_PWDN 32
#define CAM_PIN_RESET -1
#define CAM_PIN_XCLK 0
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27
#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 21
#define CAM_PIN_D2 19
#define CAM_PIN_D1 18
#define CAM_PIN_D0 5
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
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,
.pin_sccb_scl = CAM_PIN_SIOC,
.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 = 8000000, // 8MHz 低时钟频率
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, // 800x600 中等分辨率
.jpeg_quality = 12, // 较高质量(1-63)
.fb_count = 2, // 双缓冲
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST
};
static const char *TAG = "camera_example";
static esp_err_t init_camera(void)
{
// 打印当前内存状态
ESP_LOGI(TAG, "初始化前空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "摄像头初始化失败: 0x%x", err);
// 详细错误诊断
if (err == ESP_ERR_NO_MEM)
{
ESP_LOGE(TAG, "内存不足! 空闲PSRAM: %u字节",
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
}
// 打印初始化后内存状态
ESP_LOGI(TAG, "初始化后空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
return err;
}
static void take_and_process_picture(void)
{
camera_fb_t *pic = esp_camera_fb_get();
if (pic)
{
ESP_LOGI(TAG, "拍摄成功! 照片大小:%u字节", pic->len);
ESP_LOGI(TAG, "拍照后空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
esp_camera_fb_return(pic);
}
else
{
ESP_LOGE(TAG, "拍照失败");
}
}
//-------------------------------------------------------------------------------
void app_main(void)
{
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
printf("Get flash size failed");
return;
}
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
// for (int i = 10; i >= 0; i--) {
// printf("Restarting in %d seconds...\n", i);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// }
// printf("Restarting now.\n");
//fflush(stdout);
//esp_restart();
//---------------------------------------------------
init_camera();
while (1)
{
take_and_process_picture();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
//-------------------------------------------------------
}
下载需要Io0接地,下载完Io0不接重启。
现在是http网络查看
1,连wifi,同一个局域网
http_camera.c
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
#include "http_camera.h"
#include "esp_log.h"
static const char *TAG = "example:httpd_camera";
typedef struct
{
httpd_req_t *req;
size_t len;
} jpg_chunking_t;
static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len)
{
jpg_chunking_t *j = (jpg_chunking_t *)arg;
if (!index)
{
j->len = 0;
}
if (httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK)
{
return 0;
}
j->len += len;
return len;
}
void start_picture_server()
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80; // 使用不同的端口80
config.ctrl_port = 32768; // 控制端口
httpd_handle_t server = NULL;
// 启动HTTP服务器
if (httpd_start(&server, &config) == ESP_OK)
{
// 注册JPEG图像路由
httpd_uri_t jpg_uri = {
.uri = "/jpg", // 访问路径
.method = HTTP_GET,
.handler = jpg_picture_httpd_handler,
.user_ctx = NULL};
httpd_register_uri_handler(server, &jpg_uri);
ESP_LOGI(TAG, "HTTP服务器启动成功,访问 /jpg 获取图像");
}
}
esp_err_t jpg_picture_httpd_handler(httpd_req_t *req)
{
camera_fb_t *fb = NULL;
esp_err_t res = ESP_OK;
size_t fb_len = 0;
int64_t fr_start = esp_timer_get_time();
fb = esp_camera_fb_get();
if (!fb)
{
ESP_LOGE(TAG, "Camera capture failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
res = httpd_resp_set_type(req, "image/jpeg");
if (res == ESP_OK)
{
res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
}
if (res == ESP_OK)
{
if (fb->format == PIXFORMAT_JPEG)
{
fb_len = fb->len;
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
}
else
{
jpg_chunking_t jchunk = {req, 0};
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL;
httpd_resp_send_chunk(req, NULL, 0);
fb_len = jchunk.len;
}
}
esp_camera_fb_return(fb);
int64_t fr_end = esp_timer_get_time();
ESP_LOGI(TAG, "JPG: %" PRIu32 "KB %" PRIu32 "ms", (uint32_t)(fb_len / 1024), (uint32_t)((fr_end - fr_start) / 1000));
return res;
}
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
#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";
// 启动视频流服务器
void start_stream_server()
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 81; // 使用不同的端口81
config.ctrl_port = 32769; // 控制端口`
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK)
{
// 注册视频流路由
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = jpg_stream_httpd_handler,
.user_ctx = NULL};
httpd_register_uri_handler(server, &stream_uri);
ESP_LOGI(TAG, "Stream server started at http://<IP>:81/stream");
}
else
{
ESP_LOGE(TAG, "Failed to start stream server");
}
}
esp_err_t jpg_stream_httpd_handler(httpd_req_t *req)
{
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;
if (!last_frame)
{
last_frame = esp_timer_get_time();
}
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;
}
if (fb->format != PIXFORMAT_JPEG)
{
bool jpeg_converted = frame2jpg(fb, 80, &_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);
if (res != ESP_OK)
{
break;
}
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: %" PRIu32 "KB %" PRIu32 "ms (%.1ffps)",
(uint32_t)(_jpg_buf_len / 1024),
(uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
}
last_frame = 0;
return res;
}
http_camera.h
#ifndef HTTP_CAMERA_H
#define HTTP_CAMERA_H
#include "esp_err.h"
#include "esp_http_server.h"
#ifdef __cplusplus
extern "C" {
#endif
void start_picture_server(void);
void start_stream_server(void);
esp_err_t jpg_stream_httpd_handler(httpd_req_t *req);
esp_err_t jpg_picture_httpd_handler(httpd_req_t *req);
#ifdef __cplusplus
}
#endif
#endif // JPEG_HTTP_CAPTURE_H
main
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "wifi_manager.h"
#include "http_camera.h"
//----------------------------------------------------------------------
#include "esp_log.h"
#include "esp_camera.h"
#define CAM_PIN_PWDN 32
#define CAM_PIN_RESET -1
#define CAM_PIN_XCLK 0
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27
#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 21
#define CAM_PIN_D2 19
#define CAM_PIN_D1 18
#define CAM_PIN_D0 5
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
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,
.pin_sccb_scl = CAM_PIN_SIOC,
.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 = 8000000, // 8MHz 低时钟频率
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, // 800x600 中等分辨率
.jpeg_quality = 10, // 较高质量(1-63)
.fb_count = 3, // 缓冲
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST
};
static const char *TAG = "camera_example";
static esp_err_t init_camera(void)
{
// 打印当前内存状态
ESP_LOGI(TAG, "初始化前空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "摄像头初始化失败: 0x%x", err);
// 详细错误诊断
if (err == ESP_ERR_NO_MEM)
{
ESP_LOGE(TAG, "内存不足! 空闲PSRAM: %u字节",
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
}
// 打印初始化后内存状态
ESP_LOGI(TAG, "初始化后空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
return err;
}
static void take_and_process_picture(void)
{
camera_fb_t *pic = esp_camera_fb_get();
if (pic)
{
ESP_LOGI(TAG, "拍摄成功! 照片大小:%u字节", pic->len);
ESP_LOGI(TAG, "拍照后空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
esp_camera_fb_return(pic);
}
else
{
ESP_LOGE(TAG, "拍照失败");
}
}
//-------------------------------------------------------------------------------
void app_main(void)
{
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
printf("Get flash size failed");
return;
}
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
// for (int i = 10; i >= 0; i--) {
// printf("Restarting in %d seconds...\n", i);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// }
// printf("Restarting now.\n");
//fflush(stdout);
//esp_restart();
//---------------------------------------------------
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);
ESP_LOGI(TAG, "NVS初始化完成");
initialise_wifi();
// 优化WiFi性能
esp_wifi_set_ps(WIFI_PS_NONE);
esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT20);
init_camera();
vTaskDelay(500 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "picture前查看空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
//start_picture_server();
vTaskDelay(2000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "stream前查看空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
start_stream_server();
while (1)
{
//take_and_process_picture();
ESP_LOGI(TAG, "while(1)循环查看空闲内存: DRAM:%u, PSRAM:%u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
//-------------------------------------------------------
}
两个均可以同时运行,http://192.168.137.181:81/stream http://192.168.137.181/jpg
一个http配一个端口和控制口。
具体图像质量,帧数,跟缓冲区,等关系挺大,再看怎么改吧