原创 【电子DIY】ESP32-CAM网络摄像头

2024-11-27 01:12 552 4 4 分类: MCU/ 嵌入式 文集: DIY项目
前言
最近在网上研究ESP32的种类的时候,发现有一款很特别的开发板----ESP32-CAM
这款开发板是ESP32S芯片+一个OV2640摄像头(本文用的是OV3660摄像头,两个摄像头可以通用,且OV3660画质更好一点)
但是ESP32-CAM发热大,长时间运行时甚至烫手,所以我这里为了运行的稳定性,加装了一个风扇,并且绑在一张亚克力板上
利用网络服务器,服务器获取ESP32-CAM传输的图片,然后保存在服务器上,可以嵌入APP、网页等

功能演示
(一)网页显示图片

(二)可以在网页更改显示的分辨率

(三)可以嵌入到APP内,实现监控


测试数据
测试的数据为整体数据,包括风扇、主板、摄像头
(一)记录34分钟,采样3204组数据
电压稳定5V,最高电流0.323A,最低电流0.189A,平均电流0.23A
最高功耗:1.615W,最低功耗:0.945W,平均功耗1.15W

(二)能量统计,34分钟
电池容量:0.129AH == 129毫安
电池能量:0.646WH

(三)34分钟使用服务器流量14.17MB
具体流量使用不一定,因为画面的元素、质量都会影响传输的大小
还有传输的速率也会影响流量的使用,本次测试采用3S更新一次画面

(四)小风扇
电压5V,电流0.082A,功率0.41W


ESP32-CAM
①SPI Flash:32Mbit
②RAM:内部520KB+外部4MB PSRAM
③用低功耗双核32位CPU,可作应用处理器- 主频高达240MHz
④支持UART/SPI/I2C/PWM/ADC/DAC等接口-支持OV2640和OV3660摄像头
⑤内置闪光灯- 支持图片WiFI上传-支持TF卡- 支持多种休眠模式
⑥内嵌Lwip和FreeRTOS- 支持STA/AP/STA+AP 工作模式- 支持SmartConfig/AirKiss 一键配网


代码
此段代码,设置ESP32CAM为拍照模式,然后链接服务器,将图片上传到服务器中
设定3秒拍一张,实测可以0.5-1s拍一张,主要看服务器响应速度


#include 
#include 
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* ssid = "XEMOWO";
const char* password = "";

String serverName = "api.xemowo.top";   // REPLACE WITH YOUR Raspberry Pi IP ADDRESS
//String serverName = "example.com";   // OR REPLACE WITH YOUR DOMAIN NAME

String serverPath = "/api/cat_cam/cam.php";     // The default serverPath should be upload.php

const int serverPort = 80;

WiFiClient client;

// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

const int timerInterval = 3000;    // time between each HTTP POST image
unsigned long previousMillis = 0;   // last time image was sent

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(5000);
  }
  Serial.println();
  Serial.print("ESP32-CAM IP Address: ");
  Serial.println(WiFi.localIP());

  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;


  // init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 10;  //0-63 lower number means higher quality
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 12;  //0-63 lower number means higher quality
    config.fb_count = 1;
  }
  
  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }

  sendPhoto(); 
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= timerInterval) {
    sendPhoto();
    previousMillis = currentMillis;
  }
}

String sendPhoto() {
  String getAll;
  String getBody;

  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if(!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
  }
  
  Serial.println("Connecting to server: " + serverName);

  if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful!");    
    String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--RandomNerdTutorials--\r\n";

    uint32_t imageLen = fb->len;
    uint32_t extraLen = head.length() + tail.length();
    uint32_t totalLen = imageLen + extraLen;
  
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
    client.println();
    client.print(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n=0; n0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(tail);
    
    esp_camera_fb_return(fb);
    
    int timoutTimer = 1500;
    long startTimer = millis();
    boolean state = false;
    
    while ((startTimer + timoutTimer) > millis()) {
      Serial.print(".");
      delay(100);      
      while (client.available()) {
        char c = client.read();
        if (c == '\n') {
          if (getAll.length()==0) { state=true; }
          getAll = "";
        }
        else if (c != '\r') { getAll += String(c); }
        if (state==true) { getBody += String(c); }
        startTimer = millis();
      }
      if (getBody.length()>0) { break; }
    }
    Serial.println();
    client.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connection to " + serverName +  " failed.";
    Serial.println(getBody);
  }
  return getBody;
}



PHP接收图片代码
将接收到的图片,保存在文件夹内


if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Define the upload directory
    $uploadDir = 'uploads/';
    // Ensure the upload directory exists


    // Get the uploaded file
    $file = $_FILES['imageFile']['tmp_name'];
    $fileName = basename($_FILES['imageFile']['name']);
    $uploadFile = $uploadDir . $fileName;

    // Move the uploaded file to the upload directory
    if (move_uploaded_file($file, $uploadFile)) {
        echo "File is valid, and was successfully uploaded.\n";
    } else {
        echo "Possible file upload attack!\n";
    }
} else {
    echo "Invalid request method.";
}
?>

 



展示图片PHP
将图片显示在网页,可以当作网络图片插入到网页或APP内,并且支持缩放大小


//http://api.xemowo.top/api/cat_cam/photo.php?x=100&y=100
// 从URL获取图片数据
$imageUrl = "http://api.xemowo.top/api/cat_cam/uploads/esp32-cam.jpg";
$imageData = file_get_contents($imageUrl);

// 检查是否成功获取图片数据
if ($imageData === FALSE) {
    die("Failed to retrieve image data.");
}

// 创建图像资源
$image = imagecreatefromstring($imageData);

// 检查图像是否成功创建
if ($image === FALSE) {
    die("Failed to create image from string.");
}

// 获取原始图像的宽度和高度
$originalWidth = imagesx($image);
$originalHeight = imagesy($image);

// 从GET请求中获取x和y值,如果没有提供则使用默认值300
$newWidth = isset($_GET['x']) && is_numeric($_GET['x']) ? (int)$_GET['x'] : 800;
$newHeight = isset($_GET['y']) && is_numeric($_GET['y']) ? (int)$_GET['y'] : 600;

// 创建一个新的真彩色图像资源
$newImage = imagecreatetruecolor($newWidth, $newHeight);

// 复制并调整大小图像到新图像资源
// 注意:这里直接使用了$newWidth和$newHeight,没有保持纵横比
// 如果要保持纵横比,您需要使用之前注释掉的代码来计算$newWidth和$newHeight
imagecopyresampled($newImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);

// 输出图像为Base64编码的字符串
ob_start();
imagepng($newImage);
$imageBase64 = base64_encode(ob_get_clean());

// 释放图像资源
imagedestroy($image);
imagedestroy($newImage);

// 嵌入到 img 标签中
echo '$imageBase64 . '" width="' . $newWidth . '" height="' . $newHeight . '" />';
?>




作者: 小恶魔owo, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-4067534.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /4 下一条