Skip to Content

Motion Activated ESP32-CAM

Motion Activated ESP32-CAM

In this tutorial you will learn how to build a motion-activated, battery-powered ESP32-CAM (AI-Thinker) to take pictures, whenever a PIR sensor detects movement. We will discuss various PIR sensors, battery power supply options and what pitfalls to watch out for. This project is perfect for surveillance, security, or even wildlife monitoring applications.

Required Parts

Below you will find the components required to build the project. Some parts such as USB cable, micro SD Card, or SD Card reader you may already have. No need to buy it then, none of those parts is special for this project. Though, the SD card should be not larger than 16 GB but smaller ones (4GB or 8GB) work fine.

You need either the USB-TTL Shield or the FTDI USB-TTL Adapter but not both. While the ESP32-CAM module comes with USB-TTL Shield, the FTDI USB-TTL Adapter is more convenient for programming.

I listed two different types of motion sensors. If you want a small size go with the AM312. But if you want to activate the camera only at night, go with the HC-SR501, since it can easily be equipped with a light sensor.

ESP32-CAM with USB-TTL Shield

FTDI USB-TTL Adapter

Dupont wire set

Dupont Wire Set

AM312 PIR Motion Sensor

HC-SR501 PIR Motion Sensor

MicroSD Card 16GB

SD Card Reader

USB data cable

USB Data Cable

Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.

Basics of PIR Motion Sensors

There are many different methods and sensors to detect moving objects but the most common sensors are PIR (Passive InfraRed) motion sensors that detect the movement of warm objects. A typical PIR sensor consists of two Pyroelectric sensing elements and a Fresnel lens.

PIR Motion Sensor Working Principle
PIR Motion Sensor Working Principle

Pyroelectric element

The Pyroelectric elements detect heat and are arranged and wired in way that a passing object causes a positive or negative Output signal. In case of a non-moving object, the signals of the two Pyroelectric elements cancel each other out.

Fresnel lens

The Fresnel lens (white cap) focuses the weak heat radiation of a human body and makes the sensor more sensitive. You can use a PIR sensor without the Fresnel lens but will be less sensitive and may also trigger falsely more often.

Fresnel Lens for PIR Sensor
Fresnel Lens for PIR Sensor

Note that PIR elements do not work behind glass (windows) and cannot detect objects with the same temperature as the room they are operating in. Also make sure to mount the sensor horizontally for maximum sensitivity. For more info have a look here.

Calibration Time

PIR sensors typically have a calibration time during which they adjust themself to the current ambient temperature. This can take up to 15 seconds and during that time you may get false triggers. Avoid movement during the calibration time to let the sensor adjust.

Blocking and Delay Time

Furthermore, PIR Sensors have a blocking and a delay time. The blocking or locking time of a PIR sensor is the period during which the sensor ignores any motion detected after an initial trigger. This prevents the sensor from being constantly activated by continuous movement, allowing it to reset and avoid false triggers. For example, if the blocking time is set to 30 seconds, the sensor will not detect any motion during this time frame, even if there is movement in its detection range.

Delay time, on the other hand, is the time interval the sensor waits before triggering an action after detecting motion. This delay can be useful in scenarios where you want to avoid immediate responses to motion, such as turning on a light only after a few seconds of continuous movement. For instance, if the delay time is set to 10 seconds, the sensor will wait for this duration before activating a connected device or sending a signal.

Some PIR Sensor Modules allow you to adjust the blocking and delay time while others have fixed times. While there are many PIR Sensor Modules but most of them are based on the AS312 PIR Sensor, which we will discuss in the next section.

AS312 PIR Sensor

The picture below shows the image of an AS312 PIR Sensor and a Top View with the location of the two Pyroelectric elements marked. The Fresnel Lens has been removed.

AS312 PIR Sensor
AS312 PIR Sensor

The AS312 runs on 2.7-3.3V and consumes only a tiny amount of current (15 μA). It has a blocking and delay time of 2.3 seconds.

The AS312 already has a lot of electronics integrated (see Datasheet) but you would need a stable supply voltage (voltage regulator) and a few other parts to actually use it within a circuit. Therefore various PIR Sensor Modules are available that add these required parts and offer additional functionality such as delay times greater than 2.3 seconds and sensitivity control.

We will have a look at three common PIR Sensor Modules in the next section and I will provide some recommendations which ones to use with the ESP32-CAM.

AM312 PIR Module

The AM312 PIR Module is the simplest and smallest of the three PIR Sensor Modules discussed here. It has a fixed delay and blocking time of about 2 seconds and no adjustment for sensitivity.

Pinout of AM312 PIR Module
Pinout of AM312 PIR Module

It consists just of the AS312 PIR Sensor, an HT7530 voltage regulator and an output resistor (20K). See the internal schematics below. Note that there is no polarity protection and it easy to destroy the sensor when wiring it up the wrong way.

AM312 PIR Module Schematics
AM312 PIR Module Schematics

The Module has three pins. Positive power supply (VIN), ground (GND) and the output (OUT) pin. Supply voltage is between 2.7 and 12 Volts and the output is 3.3 Volts (TTL), when movement is detected. The sensing range is 3-5m and the sensing angle is about 100°.

This is the sensor, I recommend for the motion activated ESP32-CAM. It has a short delay and blocking time (2 sec), which allows us to take pictures about every 4 seconds. It can be connected directly to a ESP32-CAM GPIO pin and worked reliably in my tests.

However, if you need a greater sensing range and detection only at night, I recommend the HC-SR501, which we will discuss in the next section.

HC-SR501 PIR Module

The HC-SR501 is the motion sensor you find most often in household applications, such as motion activated night lights. You can easily recognize it by its distinct, comparatively large Fresnel lens. See picture below.

HC-SR501 PIR Module
HC-SR501 PIR Module

Schematics of HC-SR501

The schematics of the HC-SR501 module is a lot more complex than that of the AM312, since it offers adjustable sensitivity (range) and delay time. It also uses the RE200B PIR Sensor instead of the AS312 PIR Sensor. See schematics below.

HC-SR501 PIR Module Schematics
HC-SR501 PIR Module Schematics

The delay time can be changed from about 0.3 seconds to 300 seconds. There will be still a blocking time of about 2.5 seconds. However, the HC-SR501 will allow you take pictures a bit more frequently than the AS312 (3 seconds vs 4 seconds).

Setting minimum delay

Since we will control delay time via software we want to set the hardware delay time to its minimum. For that turn the left potentiometer counter-clockwise to the position depicted below. This will give you the minimum delay time of about 0.3 seconds.

Setting minimum delay time for HC-SR501
Setting minimum delay time for HC-SR501

It is best to test and try this out without the complications of an ESP32-CAM first. Have a look at our tutorial How to use HC-SR501 PIR Motion Sensor. It has a simple example circuit that shows how to switch on an LED, when the sensor detects motion.

The other potentiometer of the HC-SR501 Module allows you to change the detection range (sensitivity) from about 2m to a maximum of about 7m. The HC-SR501 also has a jumper to change the trigger mode. For more details on that see our tutorial How to use HC-SR501 PIR Motion Sensor or this Datasheet of the HC-SR501.

Night activation via LDR

The biggest advantage (apart from the greater range) of the HC-SR501 over the AM312, is that you can add an LDR (Light Detector) so that the sensor becomes active only during night. This is great for monitoring nocturnal wildlife. This function is not well documented but if you take the Fresnel Lens off you see two through-hole ports marked RL and RT. See picture below.

Light (RL) and Temperature (RT) ports on HC-SR501
Light (RL) and Temperature (RT) ports on HC-SR501

The RT (Resistor Temperature) allows you to add a thermistor, which makes the HC-SR501 less likely to tigger false alarms due to changes in ambient temperature. The RL (Resistor Light) allows you to add an LDR (light dependent resistor), which ensures that motion detection is only activated at night.

Which specific resistor value to use for the LDR will depend on how dark you want it to be, for the sensor to become active. This is something you will have to figure out by trial an error. Here is a link to an LDR kit with various resistor values you could try. As a rough guideline, the voltage divider in the schematics shown above has a resistor with 1MΩ. You probably want to pick an LDR in a similar range for a start.

Input and Outputs

The inputs and outputs of the HC-SR501 are essentially the same as the AM312. You need to provide a supply voltage at the VCC and GND pins ranging between 4.5V up to 20V and the output pin will go high (3.3V), when motion is detected. The picture below shows the Pinout.

Pinout of HC-SR501 PIR Module
Pinout of HC-SR501 PIR Module

In summary, if you want a small, simple and cheap PIR sensor go with the AM312. It will work fine. If you want better range and limit motion detection to night time, go with the HC-SR501.

In the next section, I will quickly discuss the HC-SR505, which turned out to be unsuitable and cannot be recommended for this application.

HC-SR505 PIR Module

The HC-SR505 PIR Module is a bit longer than the AM312 but uses the same AS312 PIR Sensor and has the same Pinout. See the picture below of the HC-SR505

Pinout of HC-SR505 PIR Sensor
Pinout of HC-SR505 PIR Sensor

Supply voltage is between 4.5V up to 20V and the output is TTL (3.3V). The detection range is 3-4 meters (9-12 feet) and the delay time is fixed at about 8 seconds.

Apart from the long delay time that would limit you to take a picture only every 8 second, I also found that the sensor had issues when I used a 5V power bank as supply. I tried several sensors of the same type (HC-SR505) but the output always stayed high. I suspect the high frequency ripple of the power bank caused this (more about that latter).

The HC-SR501 and AM312 had no problem with this power bank but the HC-SR505 PIR Module refused to operate correctly. Furthermore, even when running the ESP32-CAM from a 9V battery or a pair of 18650 battery, which have no ripple the HC-SR505 did not work and was always on. Again, I suspect some high frequency interference of the ESP32-CAM with the HC-SR505 to be the issue.

In short, the HC-SR505 PIR Module is not suitable for motion activated picture taking using the ESP32-CAM. Note, however, that I have used the HC-SR505 PIR Module in several other projects and I had no problems in those applications.

Next, lets quickly have a look at the ESP32-CAM module itself.

Introducing the AI-Thinker ESP32-CAM

The AI-Thinker ESP32-CAM Development Board is a compact module that combines an ESP32-S chip, a camera, a built-in flash, and a microSD card slot. The board has integrated Wi-Fi and Bluetooth and supports a OV2640 or OV7670 camera with up to 2 megapixels resolution.

Back of ESP32-CAM
Front of ESP32-CAM
Front of ESP32-CAM

You can find more details about the ESP32-CAM board and especially how to upload code to it in our tutorial Programming the ESP32-CAM. Definitely read it, since uploading code the ESP32-CAM board is tricky. In this tutorial, I will focus on implementing the motion-activation for the ESP32-CAM.

Pinout

The following picture shows the Pinout of the ESP32-CAM. Note that in theory the EPS32-CAM can run on 3.3V but unstable behavior has been reported. You can use 5V up to 15V but I wouldn’t go higher than 12V and even then the voltage regulator might get quite hot. We will talk about different power supply options in more detail later.

Pinout of the ESP32-CAM module
Pinout of the ESP32-CAM AI-Thinker module (source)

The P_OUT pin is labelled as VCC on some boards. It is a power out pin that outputs 3.3V or 5V depending on a solder pad. You cannot use this pin to power the board! Use the 5V pin for that.

Note that most of the GPIO pins of the ESP32-CAM are already in use by the camera and the SD card reader. In addition, you should avoid using GPIO1, GPIO3 and GPIO0, since they are needed for programming the board. Furthermore, GPIO0 is connected to the camera XCLK pin and should be left floating (no connected) when running the ESP32.

Finally, GPIO pins 2, 4, 12, 13, 14, and 15 are partially used by the SD Card reader. If you don’t use the SD Card reader they are available for GPIO. Typically, GPIO 13 is used for motion activation and we will use the same pin. The specifics on how to connect a PIR motion sensor to the ESP32-CAM are described in the next section.

Connecting the PIR Sensor to ESP32-CAM

Connecting the AM312 PIR Sensor Module to the ESP32-CAM is very simple. Connect a power supply (battery) with 5V (up to 12) to the 5V and GND pin of the ESP32-CAM as shown below (red and blue wires).

Connecting AM312 PIR Sensor to ESP32-CAM
Connecting v to ESP32-CAM

You can provide more than 5V to power the board. Don’t worry. A voltage regulator is connected to the 5V pin and will reduce the input voltage to the level required by the board. But don’t go over 12V otherwise the voltage regulator might get hot.

Similarly, connect the AM312 PIR Sensor Module to the power supply (red and blue wires). Watch out for correct polarity, since the AM312 has no polarity protection. Then connect the output S or OUT of the AM312 to the GPIO13 pin of the ESP32-CAM (green wire).

Don’t connect the AM312 to the 3.3V pin for power supply. In theory this should be fine but in practice I had detection issues when running the AM312 on 3.3V. If you use the HC-SR501 PIR Sensor Module instead of the AM312, connect it the same way (GND to GND, VCC to 5V-12V).

Both the AM312 and the HC-SR501 can handle the 5V to 12V supply voltage. So, you can drive the ESP32-CAM and the PIR Sensor using the same power supply (battery) with the same voltage.

Transistor Circuit

Note that there are many tutorial that propose a transistor circuit when connecting a PIR Sensor to the ESP32-CAM. This is NOT needed! The output of the PIR Sensor (even the week one of the AM312) can easily drive the GPIO input. Connecting the PIR Sensor directly to the ESP32-CAM is fine, works well and is much simpler.

Uploading Code

If you use a FTDI USB-TTL Adapter you can keep the PIR Sensor connected while uploading and testing code. If you use the USB-TTL Shield shield, you need to disconnect and reconnect the PIR sensor every time you upload and run new code, which is rather annoying.

I therefore strongly recommend to use a FTDI USB-TTL Adapter. The picture below shows how that looks like. You don’t even need a breadboard; just some Dupont wires.

FTDI Adapter and PIR Sensor connected to ESP32-CAM
FTDI Adapter and PIR Sensor connected to ESP32-CAM

Details on how to connect the FTDI USB-TTL Adapter and how to upload code can be found in our tutorial Programming the ESP32-CAM. In the next section, I’ll show you the code for the motion activated ESP32-CAM.

Code for the Motion-Activated ESP32-CAM

The following code is a complete implementation for a motion-activated ESP32-CAM. Have a quick look first to get an overview, before diving into the details.

This code puts the ESP32-CAM into deep sleep and waits for signal on GPIO13, indicating that the PIR Sensor has detected motion. If motion is detected, the ESP32-CAM wakes up, takes a single picture, stores it with a running number on the SD card and goes back to sleep.

// Motion activated ES32-CAM
// Makerguides.com

#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include "SD_MMC.h"
#include "EEPROM.h"

// 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

void configCamera() {
  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;
  config.frame_size = FRAMESIZE_UXGA;
  config.jpeg_quality = 10;
  config.fb_count = 2;
  esp_err_t err = esp_camera_init(&config);

  sensor_t* s = esp_camera_sensor_get();
  s->set_brightness(s, 0);
  s->set_contrast(s, 0);
  s->set_saturation(s, 0);
  s->set_special_effect(s, 0);
  s->set_whitebal(s, 1);
  s->set_awb_gain(s, 1);
  s->set_wb_mode(s, 0);
  s->set_exposure_ctrl(s, 1);
  s->set_aec2(s, 0);
  s->set_ae_level(s, 0);
  s->set_aec_value(s, 300);
  s->set_gain_ctrl(s, 1);
  s->set_agc_gain(s, 0);
  s->set_gainceiling(s, (gainceiling_t)0);
  s->set_bpc(s, 0);
  s->set_wpc(s, 1);
  s->set_raw_gma(s, 1);
  s->set_lenc(s, 1);
  s->set_hmirror(s, 0);
  s->set_vflip(s, 0);
  s->set_dcw(s, 1);
  s->set_colorbar(s, 0);
}

unsigned int incCounter() {
  unsigned int cnt = 0;
  EEPROM.get(0, cnt);
  EEPROM.put(0, cnt + 1);
  EEPROM.commit();
  return cnt;
}

void enableFlash(bool enable) {
  if (enable) {
    rtc_gpio_hold_dis(GPIO_NUM_4);
  } else {
    pinMode(GPIO_NUM_4, OUTPUT);
    digitalWrite(GPIO_NUM_4, LOW);
    rtc_gpio_hold_en(GPIO_NUM_4);
  }
}

void skipPictures(int n) {
  for(int i=0; i<n; i++) {
    camera_fb_t* fb = esp_camera_fb_get();
    esp_camera_fb_return(fb);
  }
}

void takePicture() {
  camera_fb_t* fb = esp_camera_fb_get();
  unsigned int cnt = incCounter();
  String path = "/img" + String(cnt) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  file.write(fb->buf, fb->len);
  file.close();
  esp_camera_fb_return(fb);
}

void deepSleep(int atleast) {
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
  rtc_gpio_pulldown_en(GPIO_NUM_13);
  rtc_gpio_pullup_dis(GPIO_NUM_13);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
  delay(atleast);
  esp_deep_sleep_start();
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  enableFlash(true);
  EEPROM.begin(8);
  SD_MMC.begin();
  configCamera();
  skipPictures(10);
  takePicture();
  enableFlash(false);
  deepSleep(5000);
}

void loop() {
}

Let’s break down the code step by step to understand how the camera setup and image capture process works.

Includes

In the first part we include the necessary libraries for controlling the camera, the SD card and the EEPROM. Also we need a few functions related to the real time clock (rtc) during deep sleep.

#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include "SD_MMC.h"
#include "EEPROM.h"

Constants

There are various models of ESP32-CAM boards. This code is specific for the AI-Thinker model but will work most other ESP32 based camera boards well, provided they have PSRAM (see model definitions here).

In the code section below we define the pins specific for the AI-Thinker model. If you have a different ESP32-CAM board you probably will need to change these constants. Here you can find the pin definitions for other supported models.

// 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

Camera Configuration

The configCamera() function sets up the camera with specific configurations such as LEDC channel, pins for data lines, clock signals, and image settings like format, size, and quality. If you are not happy with the image quality of the ESP32-CAM, you should play around with these settings.

void configCamera() {
  sensor_t* s = esp_camera_sensor_get();
  s->set_brightness(s, 0);
  s->set_contrast(s, 0);
  ...
  s->set_colorbar(s, 0);
}

Increment Counter

The incCounter() function reads and increments a counter stored in EEPROM memory. This counter is used to name the captured images sequentially. Every time it is called it increments the counter. The counter needs be stored in EEPROM so that it is not forgotten during deep sleep.

unsigned int incCounter() {
  unsigned int cnt = 0;
  EEPROM.get(0, cnt);
  EEPROM.put(0, cnt + 1);
  EEPROM.commit();
  return cnt;
}

The code is very simple. First we get the current counter via EEPROM.get(0, cnt), then we store an incremented counter via EEPROM.put(0, cnt + 1) and commit the change using EEPROM.commit(). Finally, we return the current counter. Next time the function is called it returns the incremented counter we just stored and increments it again.

Note that we need to reserve memory for the counter in EEPROM. This done later, by calling EEPROM.begin(8) in the setup() function. The counter is an unsigned integer value with a size of 4 bytes. But I play it save and reserve 8 bytes of memory – just in case.

A 4 byte unsigned integer counter can count up to 4.294.967.295 before overflowing. That is definitely large enough to uniquely enumerate all the images we want to store on the SD card.

Flash Control

The enableFlash() function enables or disables the flash based on the enable parameter. This is a tricky bit. The problem with the ESP32-CAM is that writing to the SD Card causes the flash LED to light up and when going to deep sleep afterwards the flash LED stays on.

Just writing LOW to GPIO4, where the flash LED is connected to is not sufficient. During deep sleep this output level is lost. To keep GPIO4 low during deep sleep you need to call rtc_gpio_hold_en(GPIO_NUM_4), which tells the ESP32 to preserve the state of GPIO4 during deep sleep.

But GPIO4 is also part of the SD Card interface, and we therefore can’t keep it frozen at that level. So, when the ESP32-CAM is wakes up we disable the hold of GPIO4 via rtc_gpio_hold_dis(GPIO_NUM_4) and let the ESP32-CAM to control it for taking a picture and writing to the SD card.

Before we put the ESP32-CAM into deep sleep we call enableFlash(false), which switches of the flash LED and preserves that state during deep sleep.

void enableFlash(bool enable) {
  if (enable) {
    rtc_gpio_hold_dis(GPIO_NUM_4);
  } else {
    pinMode(GPIO_NUM_4, OUTPUT);
    digitalWrite(GPIO_NUM_4, LOW);
    rtc_gpio_hold_en(GPIO_NUM_4);
  }
}

Skip Pictures

The SkipPictures(n) function captures n images and throws them away. The reason why we are doing this is the following: The camera has several automatic functions integrated, for instance automatic white balance and others that take some time and several image frames to adjust to the environment. After a restart from deep sleep, we therefore need to give the camera a bit of time to adjust.

void skipPictures(int n) {
  for(int i=0; i<n; i++) {
    camera_fb_t* fb = esp_camera_fb_get();
    esp_camera_fb_return(fb);
  }
}

If you take the first image after a restart you will find that it is of horrible quality. Typically with a strong blue tint, or to very dark or bright. However, after skipping the first few frames you mostly (but not always) get pictures of good quality.

You could also try to disable automatic white balance and other automatic functions in the camera settings, but then you will need to have a fairly stable environment to be able to tune the other settings. Otherwise, it will be difficult to get good quality pictures.

Take Picture

The takePicture() function captures an image using the camera and saves it to the SD card with a unique filename based on the counter value. First we create a frame buffer fb by calling esp_camera_fb_get(). Then we create a unique filename for the picture and store the frame buffer as a JPG picture file (img{cnt}.jpg) on the SD card. Finally, we free the memory used by the frame buffer by calling esp_camera_fb_return.

void takePicture() {
  camera_fb_t* fb = esp_camera_fb_get();
  unsigned int cnt = incCounter();
  String path = "/img" + String(cnt) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  file.write(fb->buf, fb->len);
  file.close();
  esp_camera_fb_return(fb);
}

Deep Sleep

The deepSleep() function puts the ESP32 into deep sleep mode for a specified duration after capturing the image. This helps in conserving power when the device is not actively taking pictures.

The three lines at the beginning (esp_sleep_pd_config, rtc_gpio_pulldown_en, rtc_gpio_pullup_dis) enable the internal pull-down resistor at GPIO13, where the PIR Sensor is connected. The code/ESP32-CAM works without using the internal pull-down resistor but I wanted to play it safe here.

After that we specify the wakeup source via esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1). This means if GPIO13 goes high (1 = PIR Sensor has motion detected), we wake up the ESP32-CAM from deep sleep.

Before going to deep sleep we delay for a few seconds (atleast) to avoid taking pictures too frequently. I used 5 seconds and you cannot go much quicker due to the blocking and delay times of the PIR Sensor itself. But you can certainly increase the time, e.g. 10 or 60 seconds, if you want to reduce the number of pictures taken.

void deepSleep(int atleast) {
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
  rtc_gpio_pulldown_en(GPIO_NUM_13);
  rtc_gpio_pullup_dis(GPIO_NUM_13);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
  delay(atleast);
  esp_deep_sleep_start();
}

Setup Function

The setup() function is where we glue it all together. First we call WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0) to disable the brownout detection. Should the power supply be unreliable or the ESP32-CAM temporarily draws too much power when starting WiFi, or writing to the SD card, this avoids an automatic reset. For more details see this blog post Disabling the ESP32 Brownout detector.

I did not have any problems with brownouts and you probably can disable the line. The advantage of the brownout detection is that it reduces the risk of SD card corruption. The disadvantage is, that is might cause accidental, unwanted resets. It will depend on the power supply you are using, which one is preferable.

After specifying the brownout handling, we are enabling the flash LED via enableFlash(true). Then we reserve 8 bytes of memory in the EEPROM to store the file counter, initiate the SD card with SD_MMC.begin() and configure the Camera.

Finally, we actually take the picture by calling our takePicture() function. After that we disable the flash LED and send the ESP32-CAM to deep sleep (for at least 5 seconds).

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  enableFlash(true);
  EEPROM.begin(8);
  SD_MMC.begin();
  configCamera();
  takePicture();
  enableFlash(false);
  deepSleep(5000);
}

Loop Function

The loop() function is empty in this code as the main functionality is implemented in the setup function. The ESP32 will execute the setup function, then go into deep sleep mode, wakes up when motion is detected, and runs the setup function again. The loop function never runs.

void loop() {
  // Empty loop
}

And that’s it. There you have the complete code for a motion activated ESP32-CAM.

When you run the code you may notice that the file counter starts at an arbitrary number and not a zero. You can reset the file counter by running the following code.

Reset File Counter

Uploading and running the code below will reset the file counter stored at address 0 to zero. After that you will need to upload the original, picture-taking code again.

#include "EEPROM.h"
  
void setup() {
  EEPROM.begin(512);
  unsigned int cnt = 0;
  EEPROM.put(0, cnt);
  EEPROM.commit();
}

void loop() {
}

We have the code, we have the wiring now let’s talk about the power supply for the project.

Battery Powered ESP32-CAM

Often you want to run a motion activated ESP32-CAM on battery power, e.g. for wildlife monitoring. Finding the right power source turns out to be quite tricky. In the following, I’ll show you four options, I explored.

USB Power Bank 1

The first option is to use a common USB Power Bank. Everyone with a mobile phone tends to have one of those to recharge their phone in an emergency. I tried to use the Power Bank depicted below.

USB Power Bank with automatic Shutdown
USB Power Bank with automatic Shutdown

This is a 30000mAh Power Bank that could run the ESP32-CAM for a long time. However, this Power Bank has an automatic shutdown. If the load on the output is very low (<80mA), which happens when the ESP32-CAM goes into deep sleep (6mA), the Power Bank shuts down.

The ESP32-CAM would run for about 10-15 seconds, take a few pictures and then never wake up again. It took me considerable time to figure this out, thinking my code or wiring was faulty : (

Lesson learnt, don’t use a Power Bank with automatic shutdown for this.

USB Power Bank 2

I had a second Power Bank depicted below, with 10000mAh capacity.

USB Power Bank without automatic Shutdown
USB Power Bank without automatic Shutdown

This Power Bank does not automatically shutdown but when trying it with the HC-SR505 PIR Module, I could not get the sensor to work properly. The sensor output was always high, even when using the sensor module in isolation (without the ESP32-CAM), with just the power supply and an LED.

A look on the oscilloscope revealed that this Power Bank has visible ripple on its 5V output with a frequency of about 160Hz and 88mV of amplitude. I suspect that this caused the problems with the HC-SR505 PIR Module, since this sensor worked fine when running on a 9V battery, for instance.

Output Voltage Ripple of 5V USB Power Bank

While the ESP32-CAM with an HC-SR501 or AM312 PIR Sensor worked fine with this Power Bank when tested, I was a bit concerned regarding the long term behaviour. Would this setup be reliable when running over several days or weeks?

Motion-activated ESP32-CAM powered by USB Power Bank
Motion-activated ESP32-CAM powered by USB Power Bank

I think the better option is to use a plain, rechargeable battery that do not have the internal power booster circuit of a USB Power Bank that causes these ripples in the output voltage.

9V Rechargeable Battery

To power the ESP32-CAM including the PIR Sensor you can simply use a 9V battery. Here I used a rechargeable 9V Battery with 1000mAh capacity directly connected to the 5V pin of the ESP32-CAM board and the VCC pin of the PIR sensor.

Motion-activated ESP32-CAM powered by 9V battery

This setup works nicely but the running time is not great. While the ESP32-CAM consumes only 6mA in deep sleep, it will consume up to 240mA when awake. So in deep sleep we could run for 1000mAh / 6mA = 166.67 hours = 6.9 days, which is not bad. But if we frequently take pictures the running time will be much shorter.

The short video clip below demonstrates that the ESP32-CAM with an AM312 PIR Sensor Module can run on a 9V battery.

Motion-activated ESP32-CAM powered by 9V battery in Action
Motion-activated ESP32-CAM powered by 9V battery in Action

In the next section we look at batteries with higher capacities to increase the running time.

18650 Rechargeable Battery Pack

So, let’s get a bigger battery. A 18650 lithium-ion has a nominal voltage of 3.6V and range in capacity from 1800mAh to 2800mAh. Two of them in series will give us 7.2V of output voltage, which is comfortable in range for the ESP32-CAM and allows for some voltage drop as well.

Motion-activated ESP32-CAM powered by two 18650 batteries
Motion-activated ESP32-CAM powered by two 18650 batteries

With a capacity of 2800mAh we can almost triple the running time (compared to the 1000mAh battery). And if more capacity is needed we can simply add a second pair of 18650’s and double the running time again.

But watch out for correct polarity when connecting batteries to the circuit. The 18650’s can produce high currents and I fried one of my PIR Sensors by making this mistake.

In the following section, I try to get a better estimate on the running time.

Running Time

I measured the deep sleep time of the ESP32-CAM with the AM312 PIR Sensor connected and got 3.6mA in deep-sleep. When awake and taking a picture the current draw was about 120mA.

If we use the two 18650 lithium-ion batteries in series with a total capacity of 2800mAh, we get 2800mAh / 3.6mA = 777.7 hours = 32 days of running time in deep sleep. That is a month of running time but without taking any pictures.

Let’s assume we are taking 100 pictures during the entire period. Since the ESP32-CAM is awake for 5 seconds every time we take a picture, this comes to 100 * 120mA * 5sec / 60 = 1000mAh.

If we subtract those 1000mAh from our battery capacity of 2800mAh, we end up with a running time of 1800mA / 3.6mA = 500 hours = 20 days. Almost three weeks; I think that is decent. Of course, low outside temperatures or a larger amount of pictures taken can reduce the running time drastically. On the other hand, adding a second pair of 18650 batteries is easy and doubles the running time.

Conclusions

In this tutorial, we successfully built a motion-activated ESP32-CAM using a PIR sensor. We learned about the basics of PIR motion sensors and how to connect them to the AI-Thinker ESP32-CAM. By implementing the provided code, our ESP32-CAM can now capture images whenever motion is detected.

Additionally, we explored the possibility of powering the ESP32-CAM with batteries for a more portable solution. This project opens up a range of possibilities for creating surveillance systems, wildlife cameras, or any application that requires motion detection and image capture.

The most important lessons learnt are that we need to be careful the selection of PIR Motion Sensors and which battery power solution to choose. Getting the ESP32-CAM working is not easy but the end result is worth it.

Enjoy!

Frequently Asked Questions

And here are some common questions and solutions to help you troubleshoot:

Q: Are there any specific PIR sensors that work best with the ESP32-CAM?

A: Yes, use the HC-SR501 or AM312. Do not use the HC-SR505, since its delay time is too long it has issues when connected to the ESP32-AM

Q: How can I adjust the sensitivity of the PIR sensor?

A: Some PIR sensors (including the HC-SR501) come with a potentiometer that allows you to adjust the sensitivity. By turning the potentiometer, you can fine-tune the sensor’s detection range.

Q: Can I power the ESP32-CAM with a battery?

A: Yes, you can power the ESP32-CAM with a battery for a portable setup. If using a USB Power Bank ensure that it does not have an automatic shutdown. The better option is a pair of 18650 batteries or any other battery solution that provides stable power in the 5V to 12V range.

Q: Do I need to modify the code for different PIR sensors?

A: The code provided in this tutorial is generic and should work with most PIR sensors.

Q: What is the range of a PIR Sensor for capturing motion?

A: It will depend on the specific sensor and environmental conditions but typically around 5 meters. Note that you cannot place the detector behind glass and it will not detect objects with the same temperature as the environment.

Q: Are there any alternative motion sensors that can be used with the ESP32-CAM?

A: Yes, there are other motion sensors like ultrasonic sensors, photoelectric barriers or microwave sensors that can also be used with the ESP32-CAM.

Q: How can I extend the battery life of the ESP32-CAM?

A: To extend the battery life of the ESP32-CAM, you can extend the delay time. Additionally, using a larger capacity battery will extend battery life.

Q: Why are the images taken by the ESP32-CAM have a blue tint after a restart?

A: The camera has automatic functions, for instance automatic white balance that take some time and to adjust to the environment. Without that you will get blue-tinted or very dark or bright pictures. An easy way to avoid this to take several pictures after a restart and ignore them. See the skipPictures() function in the tutorial that does exactly that.