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
- The ESP32-CAM is currently in deep sleep.
- To wake up the board, press the RESET button.
- A photograph is taken by the camera.
- The photo is saved as pictureX.jpg on the microSD card, with X corresponding to the picture number.
- 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-CAM | FTDI PROGRAMMER |
GND | GND |
5V | VCC |
U0R | TX |
U0T | RX |
GPIO 0 | GND |
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.