In this project, we’re going to make a motion sensor detector with photo capture using an ESP32-CAM. When your PIR sensor detects motion, it wakes up, takes a photo and saves it in the microSD card.

This project is very similar with a previous one, but after so many requests, we added a PIR motion sensor to the circuit. So, when motion is detected a picture is taken and saved on the microSD card.
Other ESP32-CAM projects and tutorials:
- ESP32-CAM Video Streaming and Face Recognition with Arduino IDE
- ESP32-CAM Video Streaming Web Server (Home Assistant, Node-RED, etc…)
- ESP32-CAM Take Photo and Save to MicroSD Card
- Take Photo, Save to SPIFFS and Display in Web Server
- ESP32-CAM Troubleshooting Guide
We have a similar project using a Raspberry Pi and a camera module:
Parts Required
For this project, you’ll need the following parts:
- ESP32-CAM with OV2640 – read Best ESP32-CAM Dev Boards
- MicroSD card
- PIR motion sensor
- 2N3904 transistor
- FTDI programmer
- Female-to-female jumper wires
- 5V power supply for ESP32-CAM or power bank (optional)
Project Overview

Here is a quick overview on how the project works.
- The ESP32-CAM is in deep sleep mode with external wake up enabled.
- When motion is detected, the PIR motion sensor sends a signal to wake up the ESP32.
- The ESP32-CAM takes a photo and saves it on the microSD card.
- It goes back to deep sleep mode until a new signal from the PIR motion sensor is received.
Recommended reading: ESP32 Deep Sleep with Arduino IDE and Wake Up Sources
Formatting MicroSD Card
The first thing we recommend doing is formatting your microSD card. You can use the Windows formatter tool or any other microSD formatter software.
1. Insert the microSD card in your computer. Go to My Computer and right click in the SD card. Select Format as shown in figure below.

2. A new window pops up. Select FAT32, press Start to initialize the formatting process and follow the onscreen instructions.

Note: according to the product specifications, the ESP32-CAM should only support 4 GB SD cards. However, we’ve tested with 16 GB SD card and it works well.
Installing the ESP32 add-on
We’ll program the ESP32 board using Arduino IDE. So, you need the Arduino IDE installed as well as the ESP32 add-on:
ESP32-CAM Take Photo with PIR Sketch
Copy the following code to your Arduino IDE.
/*********
Gnd_To_Vcc
IMPORTANT!!!
- Select Board "AI Thinker ESP32-CAM"
- GPIO 0 must be connected to GND to upload a sketch
- After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
#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
RTC_DATA_ATTR int bootCount = 0;
// 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);
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;
pinMode(4, INPUT);
digitalWrite(4, LOW);
rtc_gpio_hold_dis(GPIO_NUM_4);
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");
delay(500);
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);
delay(1000);
// 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);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);
Serial.println("Going to sleep now");
delay(1000);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop() {
}
This code is very similar to one of our previous ESP32-CAM projects, but it enables external wake up on GPIO 13.
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13,0);
To learn more about the code, go to the following project:
ESP32-CAM Upload Code
To upload code to the ESP32-CAM board, connect it to your computer using an FTDI programmer. Follow the next schematic diagram:

Many FTDI programmers have a jumper that allows you to select 3.3V or 5V. Make sure the jumper is in the right place to select 5V.
Important: GPIO 0 needs to be connected to GND so that you’re able to upload code.
ESP32-CAM | FTDI Programmer |
GND | GND |
5V | VCC (5V) |
U0R | TX |
U0T | RX |
GPIO 0 | GND |
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.

After a few seconds, the code should be successfully uploaded to your board.
Schematic Diagram

Assemble all the parts as shown in the following schematic diagram.

If you prefer, you can follow the Fritzing diagram instead.

To prevent problems during upload, we recommend assembling the circuit only after uploading the code.
Demonstration
After uploading de code and assembling the circuit, insert a formatted microSD card and apply power to your circuit – you can use a portable charger, for example.

Then, press the reset (RST) button, and it should start working. When it detects motion, it turns on the flash, takes a photo and saves it on the microSD card.

Experiment with this circuit several times to make sure that it is working. Then, insert the microSD card to your computer to see the captured photos.

Here’s an example:

Now you can finish this project the way you want, you can either use a dummy camera and insert your ESP32-CAM with the PIR motion sensor, or you can build your own enclosure.

You can also apply the concepts learned in this tutorial in your own projects.
Troublehsooting
If you’re getting any of the following errors, read our ESP32-CAM Troubleshooting Guide: Most Common Problems Fixed
- 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
Wrapping Up
We hope you’ve liked this project.
If there is any project you’d like to see with the ESP32-CAM or if you’d like to share your project with us, write a comment in the comment’s section below.
Thank you for reading.
12 thoughts on “ESP32-CAM PIR Motion Detector with Photo Capture (saves to microSD card)”