ESP32 S3 Camera - Save Photo to SD Card Every Few Seconds

This tutorial instructs you how to make an ESP32 S3 camera that takes a photo every few seconds and saves it to a microSD card. The board works alone. It does not need WiFi, and it does not need a computer. You only give it power, and it fills the card with photos.

ESP32 S3 Camera - Save Photo to SD Card Every Few Seconds

What you'll build:

  1. An ESP32 S3 camera that takes a photo every 30 seconds
  2. Photos saved on a microSD card as photo_00001.jpg, photo_00002.jpg, and so on
  3. Code that never writes over your old photos, even after you restart the board
  4. A time-lapse camera you can leave alone for hours

Hardware Preparation

1×ESP32 S3 N16R8 OV5640 Camera
1×Alternatively, ESP32 S3 N16R8 OV2640 Camera
1×microSD Card
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Optionally, DC Power Jack
1×Optionally, ESP32 S3 44-Pin Screw Terminal Block
1×Optionally, ESP32 S3 44-Pin Breakout Board
1×Recommended: Screw Terminal Expansion Board for ESP32 S3
1×Recommended: Breakout Expansion Board for ESP32 S3
1×Recommended: Power Splitter for ESP32 S3

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

※ NOTE THAT:

The ESP32 S3 camera board has 40 pins, but the screw terminal block and the breakout board have 44 pins. They still work together. Put the camera board in the center of the 44-pin board, so 2 pins stay free on the left side and 2 pins stay free on the right side.

The screw terminal block lets you connect wires with a screwdriver, with no soldering. The breakout board gives you easy pin headers for a breadboard.

Overview of microSD Card on ESP32 S3

The ESP32 S3 camera board has a microSD card slot. The card is a very good place for photos, because:

  • A photo is too big for the memory of the ESP32 S3. The card can hold thousands of photos.
  • The card keeps the photos when the power goes off.
  • You can take the card out and read it on your computer.
ESP32 S3 microSD Card Slot

Card Requirements

ItemValue
Card typemicroSD or microSDHC
SizeUp to 32 GB works best
File systemFAT32
Speed classClass 10 or faster

※ NOTE THAT:

A card bigger than 32 GB usually comes with the exFAT file system. The ESP32 S3 cannot read exFAT. Format the card as FAT32 before you use it.

1-Bit Mode and 4-Bit Mode

A microSD card can talk to the ESP32 S3 in two ways:

ModeData pinsSpeedCan we use it?
4-bit4 pinsFastNo. The camera already uses those pins.
1-bit1 pinSlowerYes. This is what we use.

1-bit mode is slower, but it is fast enough. A photo of 200 KB is saved in less than one second.

The code turns on 1-bit mode with the word true:

SD_MMC.begin("/sdcard", true); // true means 1-bit mode

The Pins of the Card Slot

The card slot is already connected inside the board. You do not need to wire it. But the code must know the pins:

Card PinESP32 S3 PinMeaning
CLKGPIO39Clock
CMDGPIO38Commands
D0GPIO40Data

※ NOTE THAT:

Different boards use different pins for the card slot. If your card is not found, look at the drawing of your board and change the three lines SD_CLK_GPIO_NUM, SD_CMD_GPIO_NUM and SD_D0_GPIO_NUM at the top of the code.

Wiring Diagram

There is no wiring in this project. You only put the microSD card into the slot.

The wiring diagram between ESP32 S3 Camera SD Card

This image is created using Fritzing. Click to enlarge image

  1. Unplug the USB cable.
  2. Make sure the camera module is connected with its flat cable.
  3. Push the microSD card into the slot until you hear a small click.
  4. Plug in the USB cable.

Safety Notes

Never take the card out while the board is writing a photo. The photo, and sometimes the whole card, becomes broken. Always unplug the USB cable first, wait two seconds, then take the card out.

Arduino IDE Settings for ESP32 S3 Camera

The camera does not work with the default settings of the Arduino IDE. You must change some items in the Tools menu before you upload the code.

Tools Menu ItemSelect This
BoardESP32S3 Dev Module
Flash Size16MB (128Mb)
PSRAMOPI PSRAM
Partition SchemeHuge APP (3MB No OTA/1MB SPIFFS)
CPU Frequency240MHz (WiFi)
USB ModeHardware CDC and JTAG
Upload ModeUART0 / Hardware CDC
Upload Speed921600
USB CDC On BootEnabled

※ NOTE THAT:

PSRAM must be OPI PSRAM, and Partition Scheme must be Huge APP. Change Flash Size first, because the list of Partition Scheme options changes after that.

How To Save a Photo to the SD Card

You do not need to install any library. SD_MMC is already inside the ESP32 board package.

  • Include the two files at the top of your code.
#include "FS.h" #include "SD_MMC.h"
  • Tell the library which pins the card slot uses, then start the card in 1-bit mode.
SD_MMC.setPins(39, 38, 40); // CLK, CMD, D0 if (!SD_MMC.begin("/sdcard", true)) { Serial.println("SD card not found"); }
  • Check that a card is really inside the slot.
if (SD_MMC.cardType() == CARD_NONE) { Serial.println("No SD card inside the slot"); }
  • Open a file, write the photo, and close the file. The photo is inside fb->buf, and its size is inside fb->len.
camera_fb_t *fb = esp_camera_fb_get(); File file = SD_MMC.open("/photo.jpg", FILE_WRITE); file.write(fb->buf, fb->len); file.close(); esp_camera_fb_return(fb);

※ NOTE THAT:

You must call file.close(). If you forget it, the photo stays in the memory and never reaches the card.

  • Give every photo a different name, or the new photo writes over the old one.
String makeFileName(int number) { char name[32]; sprintf(name, "/photo_%05d.jpg", number); return String(name); }

%05d means "write the number with 5 digits". So the names are photo_00001.jpg, photo_00002.jpg, ... Your computer then shows the photos in the correct order.

  • When the board restarts, the counter starts at 1 again. To keep the old photos, look for the first free name:
while (SD_MMC.exists(makeFileName(photoNumber))) photoNumber++;
  • Use millis() to wait between two photos. Do not use delay(), because delay() stops everything.
if (millis() - lastPhotoTime >= PHOTO_INTERVAL) { lastPhotoTime = millis(); takeAndSavePhoto(); }

ESP32 S3 Code

The following code takes one photo every 30 seconds and saves it to the microSD card. To change the time, change the line #define PHOTO_INTERVAL 30000. The number is in milliseconds, so 30000 means 30 seconds.

/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-camera-save-photo-to-sd-card-every-few-seconds */ #include "esp_camera.h" #include "FS.h" #include "SD_MMC.h" #define PHOTO_INTERVAL 30000 // take one photo every 30 seconds (30000 milliseconds) // Camera pins of the ESP32-S3-N16R8 camera board. // The same pins work for the OV2640, OV3660 and OV5640 versions. #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 15 #define SIOD_GPIO_NUM 4 #define SIOC_GPIO_NUM 5 #define Y9_GPIO_NUM 16 #define Y8_GPIO_NUM 17 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 12 #define Y5_GPIO_NUM 10 #define Y4_GPIO_NUM 8 #define Y3_GPIO_NUM 9 #define Y2_GPIO_NUM 11 #define VSYNC_GPIO_NUM 6 #define HREF_GPIO_NUM 7 #define PCLK_GPIO_NUM 13 // Pins of the microSD card slot on the board #define SD_CLK_GPIO_NUM 39 #define SD_CMD_GPIO_NUM 38 #define SD_D0_GPIO_NUM 40 int photoNumber = 1; unsigned long lastPhotoTime = 0; // Returns the name of the camera sensor that is installed on the board const char *getSensorName(int pid) { switch (pid) { case OV2640_PID: return "OV2640 (2 MP, max UXGA 1600x1200)"; case OV3660_PID: return "OV3660 (3 MP, max QXGA 2048x1536)"; case OV5640_PID: return "OV5640 (5 MP, max QSXGA 2592x1944)"; default: return "Unknown sensor"; } } bool initCamera() { 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_sccb_sda = SIOD_GPIO_NUM; config.pin_sccb_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; config.frame_size = FRAMESIZE_UXGA; // 1600x1200 - a big, sharp photo config.jpeg_quality = 12; // 0 to 63, a lower number means a better image config.fb_count = 1; config.fb_location = CAMERA_FB_IN_DRAM; config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; if (psramFound()) { config.fb_location = CAMERA_FB_IN_PSRAM; config.jpeg_quality = 10; config.fb_count = 2; config.grab_mode = CAMERA_GRAB_LATEST; } else { config.frame_size = FRAMESIZE_QVGA; Serial.println("WARNING: PSRAM not found. Please set PSRAM to OPI PSRAM in the Tools menu."); } esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x\n", err); return false; } sensor_t *s = esp_camera_sensor_get(); Serial.printf("Camera sensor: %s\n", getSensorName(s->id.PID)); // Each sensor needs slightly different settings switch (s->id.PID) { case OV2640_PID: s->set_vflip(s, 0); s->set_hmirror(s, 0); break; case OV3660_PID: s->set_vflip(s, 1); // the OV3660 image is upside down by default s->set_brightness(s, 1); // make the image a little brighter s->set_saturation(s, -2); // make the colors a little softer break; case OV5640_PID: s->set_vflip(s, 0); s->set_hmirror(s, 0); break; default: break; } return true; } bool initSDCard() { SD_MMC.setPins(SD_CLK_GPIO_NUM, SD_CMD_GPIO_NUM, SD_D0_GPIO_NUM); // true means 1-bit mode. The camera uses the other pins, so we cannot use 4-bit mode. if (!SD_MMC.begin("/sdcard", true)) { Serial.println("SD card not found. Please check the card and push it in again."); return false; } if (SD_MMC.cardType() == CARD_NONE) { Serial.println("No SD card inside the slot"); return false; } Serial.printf("SD card size: %llu MB\n", SD_MMC.cardSize() / (1024 * 1024)); return true; } // Makes a file name like /photo_00001.jpg String makeFileName(int number) { char name[32]; sprintf(name, "/photo_%05d.jpg", number); return String(name); } // Looks for the first free file name, so old photos are never lost void findFirstFreeNumber() { while (SD_MMC.exists(makeFileName(photoNumber))) photoNumber++; Serial.printf("The next photo will be photo_%05d.jpg\n", photoNumber); } void takeAndSavePhoto() { camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { Serial.println("Photo capture failed"); return; } String fileName = makeFileName(photoNumber); File file = SD_MMC.open(fileName.c_str(), FILE_WRITE); if (file) { file.write(fb->buf, fb->len); file.close(); Serial.printf("Saved %s (%u bytes)\n", fileName.c_str(), fb->len); photoNumber++; } else { Serial.printf("Cannot write the file %s\n", fileName.c_str()); } esp_camera_fb_return(fb); // give the memory back to the camera } void setup() { Serial.begin(115200); delay(1000); if (!initCamera()) { Serial.println("Stopped"); while (true) delay(1000); } if (!initSDCard()) { Serial.println("Stopped"); while (true) delay(1000); } findFirstFreeNumber(); takeAndSavePhoto(); // take the first photo now lastPhotoTime = millis(); } void loop() { if (millis() - lastPhotoTime >= PHOTO_INTERVAL) { lastPhotoTime = millis(); takeAndSavePhoto(); } }

Detailed Instructions

  1. New to ESP32 S3? Complete our Getting Started with ESP32 S3 guide first.
  2. Format your microSD card as FAT32 on your computer.
  3. Unplug the USB cable, then put the card into the slot on the board.
  4. Copy the above code and paste it into the Arduino IDE.
  5. Change the settings in the Tools menu. See the table above.
  6. Compile and upload the code to the ESP32 S3 board by clicking the Upload button in Arduino IDE.
Arduino IDE Upload Code
  1. Open the Serial Monitor in Arduino IDE.
How to open serial monitor on Arduino IDE
  1. Press the RESET button on the board one time.
  2. Wait some minutes. Point the camera at something that changes, for example a plant or a window.
  3. Unplug the USB cable, take the card out, and read it on your computer.
  4. Pro Tip: A time-lapse video is made from these photos. Many free programs can join the photos into one video.

Serial Monitor

The Serial Monitor tells you the name and the size of every photo. The following readings were captured in 2026:

Newbiely | Arduino IDE 2.3.8
──
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
[2026-09-23 11:00:01] Camera sensor: OV5640 (5 MP, max QSXGA 2592x1944) [2026-09-23 11:00:01] SD card size: 15193 MB [2026-09-23 11:00:01] The next photo will be photo_00001.jpg [2026-09-23 11:00:02] Saved /photo_00001.jpg (184320 bytes) [2026-09-23 11:00:32] Saved /photo_00002.jpg (183104 bytes) [2026-09-23 11:01:02] Saved /photo_00003.jpg (185876 bytes) [2026-09-23 11:01:32] Saved /photo_00004.jpg (182992 bytes)
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

When you restart the board, the counter does not start again at 1. It continues after your last photo:

Newbiely | Arduino IDE 2.3.8
──
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
[2026-09-23 11:05:00] The next photo will be photo_00005.jpg [2026-09-23 11:05:01] Saved /photo_00005.jpg (184028 bytes)
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

How Many Photos Fit on the Card?

One photo at 1600×1200 is about 180 KB. This table helps you plan:

Card SizeNumber of PhotosOne photo every 30 seconds lasts
8 GBabout 44,000about 15 days
16 GBabout 88,000about 30 days
32 GBabout 177,000about 61 days

※ NOTE THAT:

Do you want smaller photos? Change config.frame_size to FRAMESIZE_SVGA (800×600). One photo is then about 50 KB, so the card holds four times more photos.

Troubleshooting

The Serial Monitor shows SD card not found.

Try these, one by one:

  • Push the card in again until it clicks.
  • Format the card as FAT32, not exFAT and not NTFS.
  • Use a card of 32 GB or smaller.
  • Check the three pin numbers at the top of the code against the drawing of your board.

The Serial Monitor shows No SD card inside the slot.

The board found the slot but no card. Push the card in again. Also try another card, because old cards often fail.

The Serial Monitor shows Cannot write the file.

The card is full, or the card is locked, or the card is broken. Check the free space on your computer.

The photos are all black or all white.

The camera needs a moment for the light. Increase PHOTO_INTERVAL, and make sure the room is not too dark.

The Serial Monitor shows nothing after the boot messages.

If your USB cable is in the port named UART, set USB CDC On Boot to Disabled. If it is in the port named USB, set it to Enabled.

The Serial Monitor shows Camera init failed with error 0x105.

Set PSRAM to OPI PSRAM in the Tools menu, and check the flat cable of the camera.

Applications

A camera that takes a photo every few seconds and saves it to a card is the simplest way to make a time-lapse video, and it needs no WiFi. Here are practical projects you can build with this code:

  1. Plant growth time-lapse: One photo every hour shows a flower opening in a few seconds of video.
  2. Building site record: Keep a picture of the work of every day, and show it to your customer.
  3. Sky and weather time-lapse: Photograph the sky all day and watch the clouds move.
  4. 3D printer time-lapse: Record a long print job and make a short video of it.
  5. Shop and office record: Keep a photo record of the room with no internet connection.
  6. School science project: Watch ice melting, bread rising, a candle burning, or a seed growing.
  7. Sunrise and sunset video: Leave the camera at a window all day and join the photos into a video.
  8. Aquarium and terrarium watcher: See what your fish or your reptile does when nobody is in the room.
  9. Road and traffic study: Count the cars that pass your street during one day.
  10. Long machine test: Keep a photo of a machine every 30 seconds, so you can find the moment it failed.

Video Tutorial

Watch the step-by-step video walkthrough for this ESP32 S3 project below.

Challenges

  1. Beginner: Change the time between photos from 30 seconds to 5 seconds, then to 5 minutes.
  2. Intermediate: Save the photos into folders, one folder per day, for example /2026-09-23/photo_00001.jpg.
  3. Advanced: Add a real time clock (RTC) and put the real date and time into the file name.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!