In the article, we learn how to use the Arduino IDE to take images and save them to a microSD card using the ESP32-CAM board. When we hit the RESET button on the ESP32-CAM, it comes to life, takes a picture, and saves it to the microSD card.

Parts Required

  • ESP32-CAM with OV2640 – The ESP32 CAM WiFi Module Bluetooth with OV2640 Camera Module 2MP For Face Recognition includes a small-size camera module that can run independently as a minimum system with a footprint of only 40 x 27 mm; a deep sleep current of up to 6mA and is extensively used in many IoT applications.
  • MicroSD card – The microSD card is the world’s smallest consumer-oriented flash memory card. It employs a similar set of electrical connections to the regular SD card (short for Secure Digital). With the use of an adaptor, microSD cards may now be used in normal SD card slots. MicroSD was offered as a smaller storage option for mobile devices.
  • FTDI programmer
  • Female-to-female jumper wires
  • 5V power supply for ESP32-CAM or power bank

Key Features

The ESP32-CAM consists of the ESP32 chip so it shares the same specifications as that of the generic ESP32 module. The key features of the ESP32-CAM module are given below:

  • 802.11b/g/n Wi-Fi, Classic Bluetooth 4.2 and BLE
  • Consists of two 32-bit LX6 CPUs
  • Has 7 stage pipeline architecture
  • Equipped with Hall, on-chip and temperature sensor
  • Main frequency ranges from 80MHz-240MHz
  • Supports UART/SPI/I2C/PWM/ADC/DAC interfaces
  • 520 KB SRAM and 4MB PSRAM
  • 160MHz clock speed with computing power up to 600 DMIPS
  • Supports OV2640/OV7670 video cameras with built-in flash, image Wi-Fi upload, TF card, FOTA upgrades and various sleep modes
  • FreeRTOS and embedded Lwip

Project overview

  1. The ESP32-CAM is currently in deep sleep.
  2. To wake up the board, press the RESET button.
  3. A photograph is taken by the camera.
  4. The photo is saved as pictureX.jpg on the microSD card, with X corresponding to the picture number.
  5.        The picture number will be recorded in the ESP32 flash memory so that it does not get destroyed after the RESET and we can keep track of how many photos we’ve shot.

MicroSD Card Formatting

The first step you should take is to format your microSD card. You can use any microSD formatter software, including the Windows formatter.

1. Plug in your microSD card into your computer. Right-click on the SD card in My Computer. As indicated in the diagram below, select Format.

2.There is a new window that appears. Select FAT32, press Start to begin the formatting procedure, and then follow the onscreen prompts.

Take and Save Photo Sketch

#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() {
  
}

How code works

The code starts by including the necessary libraries to use the camera. We also include the libraries needed to interact with the microSD card:

#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

And the EEPROM library to save permanent data in the flash memory.

#include <EEPROM.h>

Define the number of bytes you want to access in the flash memory. Here, we’ll only use one byte that allows us to generate up to 256 picture numbers.

#define EEPROM_SIZE 1

Then, define the pins for the AI-THINKER camera module.

// 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

Initialise an int variable called picture number that will generate the photo name: picture1.jpg, picture2.jpg, and so on.

int pictureNumber = 0;

All our code is in the setup(). The code only runs once when the ESP32 wakes up (in this case when you press the on-board RESET button).

Define the camera settings:

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;

Use the following settings for a camera with PSRAM (like the one we’re using in this tutorial).

if(psramFound()){

  config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA

  config.jpeg_quality = 10;

  config.fb_count = 2;

}

If the board doesn’t have PSRAM, set the following:

else {

  config.frame_size = FRAMESIZE_SVGA;

  config.jpeg_quality = 12;

  config.fb_count = 1;

}

Initialize the camera:

// 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;

}

Initialize the microSD card:

//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;

}

The following lines take a photo with the camera:

camera_fb_t * fb = NULL;

// Take Picture with Camera

fb = esp_camera_fb_get();  

if(!fb) {

  Serial.println(“Camera capture failed”);

  return;

}

After that, initialize the EEPROM with the size defined earlier:

EEPROM.begin(EEPROM_SIZE);

The picture number is generated by adding 1 to the current number saved in the flash memory.

pictureNumber = EEPROM.read(0) + 1;

Create a route to your file on the microSD card to save the photo. The photo will be saved in the microSD card’s main directory with the file names (picture1.jpg, picture2.jpg, picture3.jpg, etc…).

String path = “/picture” + String(pictureNumber) +”.jpg”;

These next lines save the photo in the microSD card:

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();

After saving a photo, we save the current picture number in the flash memory to keep track of the number of photos taken.

EEPROM.write(0, pictureNumber);

EEPROM.commit();

When the ESP32-CAM takes a photo, it flashes the on-board LED. After taking the photo, the LED remains on, so we send instructions to turn it off. The LED is connected to GPIO 4.

pinMode(4, OUTPUT);

digitalWrite(4, LOW);

rtc_gpio_hold_en(GPIO_NUM_4);

Finally, we put the ESP32 in deep sleep.

esp_deep_sleep_start();

Because we don’t pass any argument to the deep sleep function, the ESP32 board will be sleeping indefinitely until RESET.

ESP32-CAM Upload Code

To upload code to the ESP32-CAM board, connect it to your computer using an FTDI programmer. 

A jumper on most FTDI programmers lets you choose between 3.3V and 5V. To select 5V, double-check that the jumper is in the correct position.

ESP32-CAMFTDI PROGRAMMER
GNDGND
5VVCC
U0RTX
U0TRX
GPIO 0GND

To start up, connect the ESP32-CAM’s 5V pin to the FTDI programmer’s VCC pin. Both devices’ grounds will be connected in the same way. The FTDI programmer’s TX pin will be linked to the ESP32-CAM’s UOR (GPIO3). Similarly, the RX pin of the ESP32-CAM module will be linked to the UOT (GPIO1).

In order for the ESP32-CAM module to go into flashing mode, you must also connect GPIO0 to GND. After uploading the programme sketch to the module, disconnect this connection.

Demonstration

Remove the jumper that connects GPIO 0 to GND after downloading the code.

Using a baud rate of 115200, open the Serial Monitor. Reset the ESP32-CAM by pressing the reset button. It should start up and snap a picture. When it takes a picture, it activates the flash (GPIO 4).

Check the Serial Monitor pane in the Arduino IDE to see if everything is operating properly.

After ensuring that everything is operating properly, detach the ESP32-CAM from the FTDI programmer and power it with a separate power supply.

To see the photos taken, remove the microSD card from the microSD card slot and insert it into your computer. 

Troubleshooting

  • Failed to connect to ESP32: Timed out waiting for packet header
  • Camera init failed with error 0x20001 or similar
  • Brownout detector or Guru meditation error
  • Sketch too big error – Wrong partition scheme selected
  • Board at COMX is not available – COM Port Not Selected
  • Psram error: GPIO isr service is not installed
  • Weak Wi-Fi Signal
  • No IP Address in Arduino IDE Serial Monitor
  • Can’t open web server
  • The image lags/shows lots of latency

To upload the code, follow the next steps:

1) Go to Tools > Board and select AI-Thinker ESP32-CAM.

2) Go to Tools > Port and select the COM port the ESP32 is connected to.

3) Then, click the upload button to upload the code.

4) When you start to see these dots on the debugging window as shown below, press the ESP32-CAM on-board RST button.

author avatar
Aravind S S