In this tutorial you will learn how to take photos with an ESP32-CAM when a button is pressed and save them to the SD Card. We are essentially building a simple digital camera.
Required Parts
You will need an ESP32-CAM to try out the code examples. You can get a ESP32-CAM with a USB-TTL Shield for programming or an FTDI USB-TTL Adapter. The FTDI USB-TTL Adapter is a bit more cumbersome to use but leaves the GPIO pins readily accessible. The USB-TTL Shield is easier to use but it is more difficult to connect to the GPIO pins.
If you want to use your PC to look at the pictures taken by the ESP32-CAM and saved to the SD Card, you also may need an SD Card reader.
ESP32-CAM with USB-TTL Shield
FTDI USB-TTL Adapter
Dupont Wire Set
Breadboard
SD Card Reader
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Wiring Diagram
We want to take a picture with the ESP32-CAM whenever a button is pressed. In the following wiring diagram we therefore connect a push button between the GND pin and the GPIO13 pin:
Whenever the button is pressed (closed), GPIO13 gets pulled to ground. That works since GPIO13 (and other GPIO pins) has an internal pullups that keep GPIO13 high, when nothing is connected. This means the button function is inverted (when closed GPIO13 == LOW
) and you will see this in the code section.
The following picture shows how you can build this circuit with a breadboard and the ESP32-CAM:
As you can see, I used the USB-TTL Shield and was able to make a small gap between the ESP32-CAM and the shield to connect the wires for button at GND and GPIO13:
Alternatively, you could use FTDI USB-TTL Adapter or connect the ESP32-CAM to the USB-TTL Shield via a set of Dupont-cables.
Code for Photo taking and saving
The following Arduino sketch takes a picture with the ESP32-CAM camera and saves it to the SD Card, when the button connected to GPIO13 is pressed (closed).
For this code you will need the esp32cam library. You can install it via the LIBRARY MANAGER in the Arduino IDE. Just search for “esp32cam” and press INSTALL. The picture below shows the completed installation:
Below is the complete code for our little snapshot application. Have a look first and then we will discuss its details:
#include "FS.h" #include "SD_MMC.h" #include "esp32cam.h" const byte btnPin = GPIO_NUM_13; const auto RES = esp32cam::Resolution::find(1600, 1200); void takePicAndSave() { static int cnt = 0; auto frame = esp32cam::capture(); if (frame == nullptr) { Serial.println("Capture failed!"); return; } String path = "/img" + String(cnt++) + ".jpg"; File file = SD_MMC.open(path.c_str(), FILE_WRITE); if (!file) { Serial.println("Failed to open file!"); } frame->writeTo(file); Serial.printf("Wrote: %s\n", path.c_str()); file.close(); } void enableFlash(bool enable) { digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW); } void initCamera() { using namespace esp32cam; Config cfg; cfg.setPins(pins::AiThinker); cfg.setResolution(RES); cfg.setJpeg(80); bool ok = Camera.begin(cfg); Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL"); } void initSDCard() { if (!SD_MMC.begin("/sdcard", true)) { Serial.println("SD Card Mount Failed!"); } else if (SD_MMC.cardType() == CARD_NONE) { Serial.println("No SD card inserted!"); } Serial.println("SD card ready."); } void setup() { Serial.begin(115200); initCamera(); initSDCard(); pinMode(btnPin, INPUT); pinMode(GPIO_NUM_4, OUTPUT); enableFlash(false); delay(1000); } void loop() { if (!digitalRead(btnPin)) { enableFlash(true); takePicAndSave(); enableFlash(false); delay(500); } }
Libraries
At the beginning of the code, three libraries are included:
#include "FS.h" #include "SD_MMC.h" #include "esp32cam.h"
These libraries are essential for the ESP32-CAM project. The FS.h
library provides file system operations, SD_MMC.h
enables the ESP32 to interface with SD cards over the MMC (MultiMediaCard) bus, and esp32cam.h
is a higher-level library that simplifies camera configuration and image capturing on the ESP32-CAM module.
Constants
Then, a constant for the button pin and a camera resolution setting are defined:
const byte btnPin = GPIO_NUM_13; const auto RES = esp32cam::Resolution::find(1600, 1200);
Here, btnPin
is assigned to GPIO 13, where the button is connected. You could also use GPIO 12 but in both cases you must run the SD Card interface in 1-bit mode! For more details see the More GPIO pins for ESP32-CAM tutorial.
The RES
object defines the image resolution, in this case, 1600×1200 pixels (UXGA), which will be used later to configure the camera. Here is a list of the possible values for the camera resolution, though depending on the camera not all of them may work:
- 96×96
- 160×120
- 128×128
- 176×144
- 240×176
- 240×240
- 320×240
- 320×320
- 400×296
- 480×320
- 640×480
- 800×600
- 1024×768
- 1280×720
- 1280×1024
- 1600×1200
takePicAndSave
The takePicAndSave() function is the core function that captures an image and writes it to the SD card:
void takePicAndSave() { static int cnt = 0;
The cnt
variable is declared static
, meaning it keeps its value between function calls. We use it to generate unique filenames for each photo.
auto frame = esp32cam::capture(); if (frame == nullptr) { Serial.println("Capture failed!"); return; }
The function calls esp32cam::capture()
to capture a frame from the camera. If capturing fails (i.e., frame
is nullptr
), an error message is printed, and the function exits early.
String path = "/img" + String(cnt++) + ".jpg"; File file = SD_MMC.open(path.c_str(), FILE_WRITE);
The code constructs a filename by concatenating “img” with the incrementing cnt
value and adding “.jpg”. The file is then opened on the SD card for writing.
if (!file) { Serial.println("Failed to open file!"); }
If the file could not be opened for some reason (e.g., SD card not present, file system error), it prints an error message.
frame->writeTo(file); Serial.printf("Wrote: %s\n", path.c_str()); file.close(); }
If the file opened successfully, the captured frame is written into the file, a confirmation message is printed to the Serial Monitor showing the saved filename, and finally, the file is closed to ensure data integrity.
enableFlash
The enableFlash(bool enable) function controls the ESP32-CAM’s built-in LED flash:
void enableFlash(bool enable) { digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW); }
This function sets the voltage level on GPIO 4. When enable
is true, it sets GPIO 4 to HIGH
, turning the flash LED on; otherwise, it sets it to LOW
, turning the flash off.
For more information on how to control the flash LED, see the Control ESP32-CAM Flash LED tutorial.
initCamera
The initCamera() function initializes the camera module:
void initCamera() { using namespace esp32cam; Config cfg; cfg.setPins(pins::AiThinker); cfg.setResolution(RES); cfg.setJpeg(80);
Inside initCamera()
, it first brings the esp32cam
namespace into local scope. Then, it creates a Config
object, cfg
, and configures it to use the predefined pins for an Ai-Thinker board layout, which is the typical board type for ESP32-CAM modules. It sets the camera resolution to RES
(1600×1200) and the JPEG compression quality to 80 (where 100 is best quality, and lower values mean more compression and worse quality).
bool ok = Camera.begin(cfg); Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL"); }
The Camera.begin(cfg)
function tries to start the camera using the settings specified. It prints “CAMERA OK” if successful, or “CAMERA FAIL” if not.
initSDCard
The initSDCard() function mounts the SD card:
void initSDCard() { if (!SD_MMC.begin("/sdcard", true)) { Serial.println("SD Card Mount Failed!"); } else if (SD_MMC.cardType() == CARD_NONE) { Serial.println("No SD card inserted!"); } Serial.println("SD card ready."); }
Here, SD_MMC.begin()
initializes the SD_MMC bus and tries to mount the file system under the path /sdcard
. The second argument true
enables 1-line mode, which simplifies wiring at the cost of lower speed. If mounting fails, it prints “SD Card Mount Failed!”. If the card is mounted but no card is physically inserted, it prints “No SD card inserted!”. Otherwise, it assumes everything is fine and prints “SD card ready.”
setup
The setup() function prepares everything once at boot:
void setup() { Serial.begin(115200); initCamera(); initSDCard(); pinMode(btnPin, INPUT); pinMode(GPIO_NUM_4, OUTPUT); enableFlash(false); delay(1000); }
In setup()
, the serial port is started at 115200 baud for debugging. The camera and SD card are initialized by calling the functions initCamera()
and initSDCard()
. Then, the button pin (btnPin
) is set as an input, and the flash control pin (GPIO 4) is set as an output. enableFlash(false)
ensures the flash LED starts off. A delay of 1 second (1000 milliseconds) is added at the end to let the system stabilize before starting the main loop.
loop
Finally, the loop() function constantly checks if the button is pressed:
void loop() { if (!digitalRead(btnPin)) { enableFlash(true); takePicAndSave(); enableFlash(false); delay(500); } }
In loop()
, it reads the state of btnPin
. If the button is pressed, digitalRead(btnPin)
returns LOW because the pin is pulled down when pressed. When the button is detected as pressed, the flash is turned on by calling enableFlash(true)
, then a photo is captured and saved by calling takePicAndSave()
, and finally, the flash is turned off again with enableFlash(false)
. A 500 millisecond delay follows to avoid taking multiple pictures from a single button press.
Conclusions
In this tutorial you learned how to build a little, digital camera using the ESP32-CAM. The code example sets up an ESP32-CAM to monitor a button. When the button is pressed, it turns on the flash, captures a photo at 1600×1200 resolution, saves it to the SD card under a sequential filename like /img0.jpg
, /img1.jpg
, etc., turns off the flash, and waits briefly before listening again.
If you want to put the ESP32 into deep-sleep between taking photos have a look at the Motion Activated ESP32-CAM tutorial and for video streaming see the Stream Video with ESP32-CAM article.
And if you have any questions, feel free to leave them in the comment section.
Happy Tinkering 😉
Stefan is a professional software developer and researcher. He has worked in robotics, bioinformatics, image/audio processing and education at Siemens, IBM and Google. He specializes in AI and machine learning and has a keen interest in DIY projects involving Arduino and 3D printing.