ESP32CAM实现

实验室留的,淘宝差不多反正

使用https://components.espressif.com/components/espressif/esp32-camera/versions/2.0.15

然后新建一个工程,依赖于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配一个端口和控制口。

具体图像质量,帧数,跟缓冲区,等关系挺大,再看怎么改吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值