ESP32-CAM Capture and Save Photo to Firebase Storage

This ESP32-CAM guide shows an easy way to capture photos through ESP32-CAM and upload or store them to Google Firebase storage. Firstly, we will create a Firebase project to store photos or any files. The Firebase project data will be secured by enabling email/password authentication as well. Hence, the photos captured by ESP32-CAM and uploaded to the Firebase storage will available to view only for the authorized users.

ESP32-CAM Capture Photo save to Firebase Storage

Store Photos and Files to Firebase Project Overview

The aim of this project is to capture photos using ESP32-CAM and upload them to google Firebase Storage where an authorized user will have access to them. A new image is captured and sent to the Firebase Storage whenever the ESP32-CAM board resets.

  • We will capture photos through the ESP32-CAM development board with OV2640 camera when the RST button will be pressed.
  • After capturing the photo, the photo will get saved in the ESP32-CAM’s SPIFFS files and will be fetched from the filesystem. 
  • ESP32-CAM secures a connection with Goggle Firebase through an authorized email/password. After a successful connection is established, the development board sends the latest image captured to the firebase storage.
  • The image gets uploaded to the Storage and can be viewed by the authorized user by navigating to the Firebase Console.

Follow the figure below which shows the whole project process:

ESP32-CAM firebase storage Project Overview flow diagram
Project Overview

Connecting ESP32-CAM with FTDI programmer

We will require the following components for this project:

  1. ESP32-CAM development board
  2. FTDI Programmer/ USB Serial to TTL Converter
  3. Connecting Wires
  4. External 5V power supply (optional)

Unlike the ESP32 development board, the ESP32-CAM does not come with the USB port attached to it. So to upload a program sketch to the ESP32-CAM, we will need to use an FTDI programmer (USB to TTL Serial converter).

FTDI USB to serial converter pinout diagram
FTDI Programmer

You can learn more about this FTDI cable here:

The table shows the connections between the ESP32-CAM and FTDI programmer:

ESP32-CAMFTDI Programmer
5VVCC
UOR (GPIO3)TX
UOT (GPIO1)RX
GNDGND

Connect the 5V pin of ESP32-CAM with the VCC pin of the FTDI programmer to power up. Both grounds of the two devices will be connected in common. The TX pin of the FTDI programmer will be connected with UOR (GPIO3) of ESP32-CAM. Likewise, the RX pin will be connected with the UOT (GPIO1) of the ESP32-CAM module.

Additionally, you will need to connect GPIO0 with GND to enable the ESP32-CAM module to go in flashing mode. Remove this connection after uploading the program sketch to the module.

On some ESP32-CAM boards, you will get a brown-out detector error which is due to the insufficient voltage provided by the FTDI cable. In that case, you should connect an external 5V power supply to ESP32 as shown below:

ESP32-CAM and FTDI programmer connection diagram for sketch upload
ESP32-CAM and FTDI programmer Connection diagram

Google Firebase Introduction

Google Firebase is an application development software created by Google. It can be used with any android/IOS application, IoT sensors, and web services to create and alter the data obtained from them. It is a helpful software for building mobile/web applications easily and interactively. Paid services include a Real-time database, firebase storage, hosting, and test lab. However, you can use Analytics for advanced messaging for free although you will have to create an account before accessing it.

One of the significant attributes of this software is that it is beginner friendly. It is very simple to connect your application to Google Firebase. You have to follow each step carefully. If you have any difficulty or want to access further information, take a look at the official Google Firebase Documentation.

Setting up Google Firebase Console

Before proceeding with the project let us understand the steps which have to be followed to successfully connect with the Google Firebase application.

Firstly, type https://firebase.google.com/ in your browser search tab and press enter.

This will open the Firebase main page. Click ‘Go to Console’ as highlighted in the red rectangular box.

Google Firebase Getting Started no1

You will be redirected to a new web page with the welcome message. Click the button ‘Create a Project’ as shown below.

Google Firebase Getting Started no2

The following page opens up.

Step 1: Write the name of your project. Remember to tick the Firebase term agreement. Now click ‘Continue.’

ESP32-CAM firebase storage project setting up 1

Step 2: There is no need to enable ‘Google analytics for this project’ as we do not require it. Click ‘Create project’ to proceed further.

ESP32-CAM firebase project pic 2

After a few moments, your project will be created.
Click ‘Continue’

ESP32-CAM firebase storage project setting up 2

Setting Authentication Methods

You will be redirected to the page of your newly created project. Go to Build > Authentication.

ESP32-CAM firebase storage project setting up 3

The following will open up. Click ‘Get Started’ to start the authentication process.

ESP32-CAM firebase storage project setting up 4

The following page will appear. Here we will head over to ‘Email/Password’ option as the sign-in method.

ESP32-CAM firebase storage project setting up 5

Enable the ‘Email/Password’ option by sliding the slider to the right. Then save the settings.

ESP32-CAM firebase project pic 7

This authenitcation method will now be enabled as seen below:

ESP32 and ESP8266 firebase authentication pic 8

Now, you will have to provide the Email/Password of the user that will have access to the firebase project. First, click ‘Users’ in the tab and then head over to ‘Add user.’

ESP32-CAM firebase storage project setting up 6

Specify the email and password of the user. Then click ‘Add user.’

ESP32-CAM firebase project pic 10

After adding the user, you will be able to view the email, date created and user UUID associated with the user you just authorized. You may notice that the ‘Signed in’ section is empty. This is because this particular user has not yet signed in to the Firebase project.

ESP32-CAM firebase storage project setting up 7

Creating Storage Bucket

Go to Build > Storage. Then click ‘Get Started.’

ESP32-CAM firebase storage project setting up 10

To set the cloud storage we will first he asked to set the rules to secure our data. Go for the default rules which allow all reads and writes from authenticated users.

ESP32-CAM firebase storage project setting up 11

Choose your cloud storage location and click ‘Done.’

ESP32-CAM firebase storage project setting up 12

It will take a few moments for the storage bucket to be created.

After the creation of the storage bucket, head over to the Files tab and you will be able to see the storage bucket ID. This is shown below. In our case it is “esp32-cam-project-99d52.appspot.com”

We will require it while programming our board.

ESP32-CAM firebase storage project setting up 13

Obtaining API Key

Go to the settings icon and click ‘Project Settings’.

ESP32-CAM firebase storage project setting up 8

Under the project settings, you will be able to view the API key as highlighted below. This is unique for your project. Keep it safe. We will need it while programming our ESP32-CAM development board.

ESP32-CAM firebase storage project setting up 9

We have successfully set up our ESP32-CAM Google Firebase Storage project with Email/Password authentication. Let us now move ahead and program our board.

Setting up Arduino IDE

We will use Arduino IDE to program our ESP32-CAM development board. Thus, you should have the latest version of Arduino IDE. Additionally, you also need to install the plugin for the board which you will use.

If your IDE does not have the plugin installed you can visit the link below:

Installing Google Firebase ESP Client Library

We will require the firebase ESP client library for this project. This library is compatible with both ESP32 and ESP8266 development boards. We will use the Library Manager in Arduino IDE to install it succefuuly.

Open Arduino IDE and click on Sketch > Library > Manage Libraries. Type ‘Firebase ESP Client’ in the search tab and install the latest version of Firebase Arduino Client Library for ESP8266 and ESP32 that is shown below.

Installing Firebase ESP Client library

After installation of the library, restart your IDE.

Arduino Sketch

Open your Arduino IDE and go to File > New to open a new file.

Copy the code given below in that file. You need to enter your network credentials. You also have to provide your web API key and storage bucket ID which were saved previously. Moreover, you will have to specify the email and password that you used in Firebase authentication to authorize the user.

#include "WiFi.h"
#include "esp_camera.h"
#include "Arduino.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  
#include "driver/rtc_io.h"
#include <SPIFFS.h>
#include <FS.h>
#include <Firebase_ESP_Client.h>
#include <addons/TokenHelper.h>

//Enter your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

#define API_KEY "AIzaSyD-TpL6e8tFBQYJSa_ouEi3RAepCSfc***"

// ENter Authorized Email and Password
#define USER_EMAIL "WRITE_AUTHORIZED_EMAIL"
#define USER_PASSWORD "WRITE_AUTHORIZED_PASSWORD"

// Enter Firebase storage bucket ID
#define STORAGE_BUCKET_ID "WRITE_YOUR_BUCKET_ID"

#define IMAGE_PATH "/pictures/image.jpg"

// OV2640 camera module pins (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

boolean new_image = true;

FirebaseData fbdo;
FirebaseAuth authentication;
FirebaseConfig configuration;

bool done = false;

bool check_photo( fs::FS &fs ) {
  File f_pic = fs.open( IMAGE_PATH );
  unsigned int pic_sz = f_pic.size();
  return ( pic_sz > 100 );
}

// Capture Photo and Save it to SPIFFS
void captureSave_photo( void ) {
  camera_fb_t * fb = NULL; 
  bool ok = 0; 
  do {
    Serial.println("ESP32-CAM capturing photo...");

    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Failed");
      return;
    }
    Serial.printf("Picture file name: %s\n", IMAGE_PATH);
    File file = SPIFFS.open(IMAGE_PATH, FILE_WRITE);
    if (!file) {
      Serial.println("Failed to open file in writing mode");
    }
    else {
      file.write(fb->buf, fb->len); 
      Serial.print("The picture has been saved in ");
      Serial.print(IMAGE_PATH);
      Serial.print(" - Size: ");
      Serial.print(file.size());
      Serial.println(" bytes");
    }
    file.close();
    esp_camera_fb_return(fb);
    ok = check_photo(SPIFFS);
  } while ( !ok );
}

void setup() {
  Serial.begin(115200);
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  if (!SPIFFS.begin(true)) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    ESP.restart();
  }
  else {
    delay(500);
    Serial.println("SPIFFS mounted successfully");
  }

  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

 // initialize OV2640 camera module
  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;

  if (psramFound()) {
    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;
  }

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

  configuration.api_key = API_KEY;
  authentication.user.email = USER_EMAIL;
  authentication.user.password = USER_PASSWORD;
  configuration.token_status_callback = tokenStatusCallback; 

  Firebase.begin(&configuration, &authentication);
  Firebase.reconnectWiFi(true);
}

void loop() {
  if (new_image) {
    captureSave_photo();
    new_image = false;
  }
  delay(1);
  if (Firebase.ready() && !done){
    done = true;
    Serial.print("Uploading Photo... ");

    if (Firebase.Storage.upload(&fbdo, STORAGE_BUCKET_ID, IMAGE_PATH, mem_storage_type_flash, IMAGE_PATH, "image/jpeg" ){
      Serial.printf("\nDownload URL: %s\n", fbdo.downloadURL().c_str());
    }
    else{
      Serial.println(fbdo.errorReason());
    }
  }
}

How the Code Works?

We will start off by including all the necessary libraries required for this project.

#include "WiFi.h"
#include "esp_camera.h"
#include "Arduino.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  
#include "driver/rtc_io.h"
#include <SPIFFS.h>
#include <FS.h>
#include <Firebase_ESP_Client.h>
#include <addons/TokenHelper.h>

Next, we will create two global variables, one for the SSID and another for the password. These will hold our network credentials which will be used to connect to our wireless router. Replace both of them with your network credentials to ensure a successful connection.

//Enter your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

Next, we will define the Google firebase web API key which we accessed and saved previously

#define API_KEY "AIzaSyD-TpL6e8tFBQYJSa_ouEi3RAepCSfc***"

The next step is to specify the user email and password that we used while authorizing the user in our firebase project. Both of these should correspond to the ones you saved while adding the user.

// Enter Authorized Email and Password
#define USER_EMAIL "WRITE_AUTHORIZED_EMAIL"
#define USER_PASSWORD "WRITE_AUTHORIZED_PASSWORD"

We will now define the storage bucket ID that we accessed and saved previously. In our case it is “esp32-cam-project-99d52.appspot.com”.

// Enter Firebase storage bucket ID
#define STORAGE_BUCKET_ID "WRITE_YOUR_BUCKET_ID"

Moreover, we will also define the image path with its name for the image to get saved on the ESP32 SPIFFS.

#define IMAGE_PATH "/pictures/image.jpg"

The following definitions are for OV2640 camera module pins. We are using CAMERA_MODEL_AI_THINKER.

// OV2640 camera module pins (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

We will define a Boolean variable called ‘new_image’ and set it as true. This will be used later on in the code to check if a new photo is captured.

boolean new_image = true;

Next, we will create some objects for some firebase functionalities: data, authentication and configuration.

FirebaseData fbdo;
FirebaseAuth authentication;
FirebaseConfig configuration;

This will check if a successful connection with Firebase has been established or not.


bool done = false;

Capturing Photo and saving on SPIFFS

The check_photo() function returns true if a valid picture got saved on the SPIFFS. This is achieved by opening the file on the photo path specified and checking its size.

The captureSave_photo() function is responsible to capture the photo and then save it on the SPIFFS on the photo path specified.

bool check_photo( fs::FS &fs ) {
  File f_pic = fs.open( IMAGE_PATH );
  unsigned int pic_sz = f_pic.size();
  return ( pic_sz > 100 );
}

// Capture Photo and Save it to SPIFFS
void captureSave_photo( void ) {
  camera_fb_t * fb = NULL; 
  bool ok = 0; 
  do {
    Serial.println("ESP32-CAM capturing photo...");

    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Failed");
      return;
    }
    Serial.printf("Picture file name: %s\n", IMAGE_PATH);
    File file = SPIFFS.open(IMAGE_PATH, FILE_WRITE);
    if (!file) {
      Serial.println("Failed to open file in writing mode");
    }
    else {
      file.write(fb->buf, fb->len); 
      Serial.print("The picture has been saved in ");
      Serial.print(IMAGE_PATH);
      Serial.print(" - Size: ");
      Serial.print(file.size());
      Serial.println(" bytes");
    }
    file.close();
    esp_camera_fb_return(fb);
    ok = check_photo(SPIFFS);
  } while ( !ok );
}

setup()

Inside the setup() function, we will open a serial connection at a baud rate of 115200.

Serial.begin(115200);

The following section of code will connect our ESP32-CAM board with the local network whose network credentials we already specified above. We will use the WiFi.begin() function. The arguments will be the SSID and the password which we defined earlier in the code. After a successful connection is established, the IP address gets displayed on the serial monitor. We will use this IP address to access our web server. SPIFFS will also get initialized and if there is error in the initialization then the ESP board will be restarted.

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  if (!SPIFFS.begin(true)) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    ESP.restart();
  }
  else {
    delay(500);
    Serial.println("SPIFFS mounted successfully");
  }

The following code sets the OV2640 camera module and the settings required for the photo capturing.

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

 // initialize OV2640 camera module
  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;

  if (psramFound()) {
    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;
  }

Initializing ESP32-CAM:

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

Now, first we will use the firebase data object we created for configuration and set the API key. Then we will specify the user email and password to the firebase authentication object.

configuration.api_key = API_KEY;
  authentication.user.email = USER_EMAIL;
  authentication.user.password = USER_PASSWORD;
 

The following line of code will be used to set the call-back function for long running token generation task. This will be set to the Firebase configuration object as well.

 configuration.token_status_callback = tokenStatusCallback; 

Using Firebase.begin() on the configuration and authentication paths, we will initialize the firebase library connection.

  Firebase.begin(&configuration, &authentication);
  Firebase.reconnectWiFi(true);

loop()

Inside the loop() function we will check if the ESP32-CAM reset, then call the captureSave_photo() function and set the ‘new_image’ variable to false. The captureSave_photo() will be used to capture the photo by the ESP32-CAM and then save it on the SPIFFS filesystem.

  if (new_image) {
    captureSave_photo();
    new_image = false;
  }
  delay(1);

Upload the image to Firebase Storage using Firebase.Storage.upload(). It takes in six arguments:

  1. &fbdo
  2. storage bucket ID
  3. current path of the photo
  4. storage type
  5. path of the photo in Firebase storage
  6. media type
  if (Firebase.ready() && !done){
    done = true;
    Serial.print("Uploading Photo... ");

    if (Firebase.Storage.upload(&fbdo, STORAGE_BUCKET_ID, IMAGE_PATH , mem_storage_type_flash, IMAGE_PATH , "image/jpeg" )
    {
      Serial.printf("\nDownload URL: %s\n", fbdo.downloadURL().c_str());
    }
    else{
      Serial.println(fbdo.errorReason());
    }
  }

ESP32 CAM Firebase Storage Demo

Now, we are ready to compile and upload the code to our ESP32-CAM. Make sure the FTDI programmer is properly connected with the module and GPIO0 is grounded as well.

Choose the correct board and COM port before uploading your code to the ESP32-CAM board. Go to Tools > Board and select ESP32 AI Thinker.

select ESP32 CAM AI thinker board in Arduino IDE

Next, go to Tools > Port and select the appropriate port through which your board is connected.

Click on the upload button to upload the code into the ESP32-CAM board.

If you view Connecting….._____….._____….. in the error window, press the RESET button present on the ESP32-CAM as shown below:

ESP32 CAM reset button

After you have successfully uploaded your code to the board, remove the connecting wire from GPIO0 and GND.

Now open the serial monitor. In a few moments, the Wi-Fi will get connected, the image will get captured and saved in SPIFFS, then successful connection with Firebase and sending the image to Firebase storage.

ESP32-CAM firebase storage project demo serial monitor
Serial Monitor

In your Firebase project go to Storage > Files

You will be able to view the pictures folder. Click this folder to view the captured image.

ESP32-CAM firebase storage project demo 1

The captured photo will be present in this folder as image.jpg.

ESP32-CAM firebase storage project demo 2

Click this .jpg file to view the photo along with some other details.

ESP32-CAM firebase storage project demo 3

Thus, we were successfully able to send our ESP32-CAM captured photo to Firebase Storage.

You may also like to read:

Leave a Comment