Make a cheap time-lapse camera using esp32 cam

Esp-32 Camera is a cheap development board with a camera, wifi, and an SD card module on it.
It can be used as a standalone device as well as with other components to make several cool camera
projects which include:
  1. IP camera for home security,
  2. motion detection alarm system, 
  3. face recognition door lock system,
  4. video recorder,
  5. selfie camera,
  6. drone camera,
  7. and other computer vision (ai) related projects.
Obviously, it can be used in many more applications, but here, I am going to discuss how you can use it as a standalone device
to record a beautiful time-lapse video.

Things you will need for this project:
  1. Esp32 cam development board,
  2. An FTDI programmer,
  3. An SD card,
  4. An SD card reader, and
  5. Some jumper wires.

Do-it-yourself Steps:

1. First thing first, you need to open Arduino IDE and go to Tools -> Boards -> Boards Manager and search esp32.

2. Install esp32 by Espressif Systems.

3. Make below connections between esp32 cam and FTDI programmer:

Esp-32 Cam FTDI Programmer
VCC -> 3V3
U0R -> TXD
U0T -> RXD

4. Insert SD card into esp32 cam’s SD card module and connect the FTDI programmer to your computer via USB.

5. In Arduino IDE, go to FIle -> New and paste the below code in it:


#include "FS.h"
#include "SD_MMC.h"
#include "esp_camera.h"
#include <eeprom.h>          // read and write from flash memory

// Select camera pin configuration
#include "camera_pins.h"

// define number of bytes to set aside in persistant memory
#define EEPROM_SIZE 1
long pictureNumber = 0;

static esp_err_t get_image(fs::FS &fs, const char * path) {
    // Variable definitions for camera frame buffer
    camera_fb_t * fb = NULL;
    int64_t fr_start = esp_timer_get_time();

    // Get the contents of the camera frame buffer
    fb = esp_camera_fb_get();
    if (!fb) {
        Serial.println("Camera capture failed");
        return ESP_FAIL;

    // Save image to file
    size_t fb_len = 0;
    fb_len = fb->len;
    File file =, FILE_WRITE);
      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);

    // Release the camera frame buffer
    int64_t fr_end = esp_timer_get_time();
    Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
    return ESP_OK;

void setup() {
//  Serial.setDebugOutput(true);

  // Initial camera configuration
  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; // This is very important for saving the frame buffer as a JPeg

  //init with high specs to pre-allocate larger buffers
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);

You may need to adjust these settings to get a picture you like. 
Look in app_httpd.cpp in the CameraWebServer as an example on how these are used.
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "framesize")) {
    if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);

  Serial.println("Camera initialized!");

    Serial.println("Card Mount Failed");
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD_MMC card attached");
  Serial.println("SD Card Initialized");

  // initialize EEPROM with predefined size
  pictureNumber = + 1;

void loop() {
  pictureNumber = pictureNumber + 1;
  // Call function to capture the image and save it as a file
  String path = "/picture" + String(pictureNumber) + ".jpg";
  if(get_image(SD_MMC, path.c_str()) != ESP_OK ) {
    Serial.println("Failed to capture picture");


6. Save the file and create a new tab by pressing Ctrl+Shift+N and name it camera_pins.h

7. In camera_pins.h, paste the below code:

#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#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      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

#elif defined(CAMERA_MODEL_ESP_EYE)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    4
#define SIOD_GPIO_NUM    18
#define SIOC_GPIO_NUM    23

#define Y9_GPIO_NUM      36
#define Y8_GPIO_NUM      37
#define Y7_GPIO_NUM      38
#define Y6_GPIO_NUM      39
#define Y5_GPIO_NUM      35
#define Y4_GPIO_NUM      14
#define Y3_GPIO_NUM      13
#define Y2_GPIO_NUM      34
#define VSYNC_GPIO_NUM   5
#define HREF_GPIO_NUM    27
#define PCLK_GPIO_NUM    25

#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23

#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM        5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       32
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     22
#define SIOC_GPIO_NUM     23

#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM        5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       32
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

#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

#error "Camera model not selected"


8. Now save camera_pins.h. Make sure the main file and camera_pins.h lie in the same directory, otherwise the program
will not compile.

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

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

11. Now, before uploading the code to esp32 cam, you need to set it to programmable mode. In order to do that connect
a jumper cable between esp32 cam’s IO0 and GND pins. You need to do this connection every time you upload your
code and remove it in order to change the mode to runnable after uploading is completed.

12. Click the upload button to upload the code.

13. Now, when uploading finishes, remove that connection between esp32 cam’s IO0 and GND pins to switch off programming mode.

14. Now, your esp32 cam board will start taking images one by one and store it on the memory card.

15. After some time, remove the FTDI programmer from the USB to power off the device and take out the SD card from the
esp32 cam development board.

16. Insert this SD card into a Card Reader and plug it in your computer to see its contents. You will find a number of images
on this card.

17. Scroll through the images, and delete the last one, as it may have been corrupted due to abrupt power loss.

18. Now you need a software to compile these images into a timelapse video. Do a normal google search and you will find
plenty of free software that takes images as input and output a video at any frame rate set by you. More will be the
frame rate, faster will the time-lapse video be.

Hope you have liked this article, please also support me on youtube. Thanks.

Buy Me a Coffee -


  1. Hi there,

    I wanted to try your code but I think there is an include missing at the beginning...

    #include "FS.h"
    #include "SD_MMC.h"
    #include "esp_camera.h"
    #include // read and write from flash memory

    As you commented in the code...
    What should be called here please ?

    Peter Lunk

    1. This comment has been removed by the author.

    2. This comment has been removed by the author.

    3. I have updated the blog. Thanks for pointing that out.

  2. This comment has been removed by the author.


Post a Comment