本文旨在试用ESP32-CAM模块,并没有搞开发。有时候有的需求需要涉及到使用摄像头,当然你可以用专业的摄像头,然后使用协议来控制,对于简单的应用来说成本高了不少。
说是可以连接摄像头,自己画个板子,打个样,似乎太费时间了,ESP32-CAM就是很好的选择来着,简单到我本文都没啥好写的了,全程低能。

用这个开发板,使用Arduino IDE可以很快的实现一些简单的功能。因为板载了摄像头模块,micro SD卡座,LED补光灯(闪光灯),几个基本的联动功能很容易实现。比如有人经过,通过人体传感器触发,就自动拍照保存到内存卡,或者上传到服务器。并且就东西本身来说,集成度越高,价格就越便宜。

安装步骤人家已经写了,我就不怎么写了:项目(二) esp32-cam 网页图像人脸

如果使用过ESP8266,使用这个模块几乎就没啥不同。下面是官方文档里面介绍的一些特性,我当然是赞同的,因为基于ESP32的,那当然有ESP32的所有优点了。

  • 采用低功耗双核32位CPU,可作应用处理器
  • 主频高达240MHz,运算能力高达 600 DMIPS
  • 内置 520 KB SRAM,外置8MB PSRAM
  • 支持UART/SPI/I2C/PWM/ADC/DAC等接口
  • 支持OV2640和OV7670摄像头,内置闪光灯
  • 支持图片WiFI上传
  • 支持TF卡
  • 支持多种休眠模式。
  • 内嵌Lwip和FreeRTOS
  • 支持 STA/AP/STA+AP 工作模式
  • 支持 Smart Config/AirKiss 一键配网
  • 支持二次开发

在本文之前,我们已经玩过了多块了乐鑫的开发板,ESP8266和ESP32,有兴趣的可以看看:

基于Blinker的ESP8266+CC2541蓝牙接入以及其他
(暂未实践)分析使用ESP32将有线键盘转换为蓝牙无线键盘
ESP8266使用MQTT连接阿里云物联网平台(官方NONOS_SDK和Arduino_IDE )
使用ESP8266自制廉价Gokit SOC方案

完整的官方介绍页:

ESP32-CAM摄像头开发板-安信可产品介绍页

ESP32-CAM是安信可最新发布小尺寸的摄像头模组。该模块可以作为最小系统独立工作,尺寸仅为2740.54.5mm,深度睡眠电流最低达到6mA。 ESP32-CAM可广泛应用于各种物联网场合,适用于家庭智能设备、工业无线控制、无线监控、QR无线识别,无线定位系统信号以及其它物联网应用,是物联网应用的理想解决方案。

ESP32-CAM采用DIP封装,直接插上底板即可使用,实现产品的快速生产,为客户提供高可靠性的连接方式,方便应用于各种物联网硬件终端场合。

体积超小的802.11b/g/n Wi-Fi + BT/BLE SoC模块

引脚定义

20200517005924535_760964942.webp

20200517005936748_1272662309.webp

最小系统图

011052.webp

011514.webp

功耗:

我自己测试也是得到的这个数据。

  • 关闭闪光灯:180mA@5V
  • 开启闪光灯并且亮度调到最大:310mA@5V
  • Deep-sleep:最低功耗可以达到 6mA@5V
  • Moderm-sleep:最低可达到 20mA@5V
  • Light-sleep:最低可达到 6.7mA@5V

实物图:

IMG_20200517_000011.webp

IMG_20200517_000004.webp

IMG_20200517_000152.webp

烧录

当然要加入ESP32的官方源,并在开发板中找到安信可的ESP32-CAM。

20200517000932998_1691872300.webp

使用例程CameraWebServer,就是官方自带的相机例子,也带有人脸识别功能。

20200517001027098_466776173.webp

在写入模块以前,要把宏定义修改一下,切换为ai-thinker,也就是安信可的那一项,不然会提示不支持。

20200517001109351_349377120.webp

同时修改要连接的Wi-Fi网络。

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

就这样就算是配置完成了。在烧录的时候要注意,要将开发板中的GPIO0连接到GND,烧录完毕后去掉连接。其他的RX,TX连接就不多说了。

测试

前面的操作一切正常以后,就可以开始试用了。串口会输出连接到的局域网IP地址,你也可以查看路由器分配的多少。

20200517000323880_1587310051.webp

进入网页就是下面的样子。

20200517000522500_442288008.webp

20200517000441527_1878164123.webp

当然可以更改视频的尺寸。

20200517000539149_1093648189.webp

在串口端会有如下输出。

20200517000649629_632577850.webp

拍照并保存到内存卡

上面的Arduino自带的例子,对于视频流的暂停,只是网页上的暂停,也不能保存到内存卡,所以要实现摄像头的内容保存到内存卡,还需要自己捣鼓一下,下面提供了一个现成的Demo,可以供参考。

ESP32-CAM Take Photo and Save to MicroSD Card

这个案例实现的是模块上电拍照,保存到内存卡,然后深度休眠。

我直接把代码照搬过来了。

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-cam-take-photo-save-microsd-card
  
  IMPORTANT!!! 
   - Select Board "AI Thinker ESP32-CAM"
   - GPIO 0 must be connected to GND to upload a sketch
   - After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h"                // SD Card ESP32
#include "SD_MMC.h"            // SD Card ESP32
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h>            // read and write from flash memory

// define the number of bytes you want to access
#define EEPROM_SIZE 1

// Pin definition for 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

int pictureNumber = 0;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  //Serial.println();
  
  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; 
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  
  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  
  //Serial.println("Starting SD Card");
  if(!SD_MMC.begin()){
    Serial.println("SD Card Mount Failed");
    return;
  }
  
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD Card attached");
    return;
  }
    
  camera_fb_t * fb = NULL;
  
  // Take Picture with Camera
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  }
  // initialize EEPROM with predefined size
  EEPROM.begin(EEPROM_SIZE);
  pictureNumber = EEPROM.read(0) + 1;

  // Path where new picture will be saved in SD Card
  String path = "/picture" + String(pictureNumber) +".jpg";

  fs::FS &fs = SD_MMC; 
  Serial.printf("Picture file name: %s\n", path.c_str());
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file in writing mode");
  } 
  else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.printf("Saved file to path: %s\n", path.c_str());
    EEPROM.write(0, pictureNumber);
    EEPROM.commit();
  }
  file.close();
  esp_camera_fb_return(fb); 
  
  // Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  rtc_gpio_hold_en(GPIO_NUM_4);
  
  delay(2000);
  Serial.println("Going to sleep now");
  delay(2000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop() {
  
}

此处代码最后的休眠是写的esp_deep_sleep_start();,直接休眠不再启动了,除非再次复位。如果需要GPIO唤醒,则写:

  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);

  Serial.println("Going to sleep now");
  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");

使用GPIO13来唤醒(因为ESP32的IO本来就不多,又有内存卡又有摄像头已经占用了很多了),不过设置为的是低电平唤醒,那就得加个三极管处理一下。

ESP32-CAM-PIR-motion-sensor-fixed.webp

摄像头发紫或仅显示一部分

如果拍照出来的图片是摄像头发紫或仅显示一部分,就降低画质。

picture133.webp

代码中有判断psramFound,存在的话,是一个设置,不存在是另外个画质。根据实际情况降低就行了。

  if (psramFound()) {
    Serial.println("psramFound!");
    config.frame_size = FRAMESIZE_SVGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 5;
    config.fb_count = 2;
  } else {
    Serial.println("psramNotFound!");
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

具体就是调节frame_size jpeg_quality,适当调低就行了。

文章目录