ESP32-CAM: Take and Send Photos via Email using an SMTP Server

This tutorial shows how to send captured photos from the ESP32-CAM to your email account using an SMTP Server. We’ll show you a simple example that takes a photo when the ESP32 boots and sends it as an attachment in an email. The last photo taken is temporarily saved in the ESP32 LittleFS filesystem.

ESP32-CAM Take and Send Photos via Email using an SMTP Server Arduino IDE

Updated 19 September 2023.

To make this project work, the ESP32-CAM needs to be connected to a router with access to the internet – needs to be connected to your local network.

This project is compatible with any ESP32 camera board with the OV2640 camera. You just need to make sure you use the right pin assignment for the board you’re using: ESP32-CAM Camera Boards: Pin and GPIOs Assignment Guide.

ESP Mail Client Library

To send emails with the ESP32-CAM, we’ll use the ESP-Mail-Client library. This library allows the ESP32 to send and receive emails with or without attachments via SMTP and IMAP servers.

In this project, we’ll use SMTP to send an email with an attachment. The attachment is a photo taken with the ESP32-CAM.

SMTP means Simple Mail Transfer Protocol and it is an internet standard for email transmission. To send emails through code, you need to know your SMTP server details. Each email provider has a different SMTP server.

Installing the ESP-Mail-Client Library

Before proceeding with this tutorial, you need to install the ESP-Mail-Client library.

Go to Sketch > Include Library > Manage Libraries and search for ESP Mail Client. Install the ESP Mail Client library by Mobizt.

Install ESP Mail Client Library Arduino IDE

Sender Email (New Account)

We recommend creating a new email account to send the emails to your main personal email address. Do not use your main personal email to send emails via ESP32. If something goes wrong in your code or if by mistake you make too many requests, you can be banned or have your account temporarily disabled.

We’ll use a newly created Gmail.com account to send the emails, but you can use any other email provider. The receiver email can be your personal email without any problem.

Create a Sender Email Account

Create a new email account for sending emails with the ESP32. If you want to use a Gmail account, go to this link to create a new one.

Gmail Create a new account

Create an App Password

You need to create an app password so that the ESP32 is able to send emails using your Gmail account. An App Password is a 16-digit passcode that gives a less secure app or device permission to access your Google Account. Learn more about sign-in with app passwords here.

An app password can only be used with accounts that have 2-step verification turned on.

  1. Open your Google Account.
  2. In the navigation panel, select Security.
  3. Under “Signing in to Google,” select 2-Step Verification > Get started.
  4. Follow the on-screen steps.

After enabling 2-step verification, you can create an app password.

  1. Open your Google Account.
  2. In the search panel, search for App Passwords.
  3. Open the App Passwords menu.
Google account create app password to send email with esp32
  1. Give a name to the app password, for example ESP. Then, click on Create. It will pop-up a window with a password that you’ll use with the ESP32 or ESP8266 to send emails. Save that password (even though it says you won’t need to remember it) because you’ll need it later.
Create app password to use with the ESP

It will pop-up a window with a password that you’ll use with the ESP32 to send emails. Save that password (even though it says you won’t need to remember it) because you’ll need it later.

Generated app password Google Account

Now, you should have an app password that you’ll use on the ESP32 code to send the emails.

Gmail app password created for ESP32 send emails

If you’re using another email provider, check how to create an app password. You should be able to find the instructions with a quick google search “your_email_provider + create app password”.

Gmail SMTP Server Settings

If you’re using a Gmail account, these are the SMTP Server details:

  • SMTP Server: smtp.gmail.com
  • SMTP username: Complete Gmail address
  • SMTP password: Your Gmail password
  • SMTP port (TLS): 587
  • SMTP port (SSL): 465
  • SMTP TLS/SSL required: yes

Outlook SMTP Server Settings

For Outlook accounts, these are the SMTP Server settings:

  • SMTP Server: smtp.office365.com
  • SMTP Username: Complete Outlook email address
  • SMTP Password: Your Outlook password
  • SMTP Port: 587
  • SMTP TLS/SSL Required: Yes

Live or Hotmail SMTP Server Settings

For Live or Hotmail accounts, these are the SMTP Server settings:

  • SMTP Server: smtp.live.com
  • SMTP Username: Complete Live/Hotmail email address
  • SMTP Password: Your Windows Live Hotmail password
  • SMTP Port: 587
  • SMTP TLS/SSL Required: Yes

If you’re using another email provider, you need to search for its SMTP Server settings. Now, you have everything ready to start sending emails with the ESP32-CAM.

Code – ESP32-CAM Send Email

The following code takes a photo when the ESP32-CAM first boots and sends it to your email account. Before uploading the code, make sure you insert your sender email settings as well as your recipient email.

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp32-cam-projects-ebook/
  
  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 "SPI.h"
#include "driver/rtc_io.h"
#include <ESP_Mail_Client.h>
#include <FS.h>
#include <WiFi.h>

// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// To send Email using Gmail use port 465 (SSL) and SMTP Server smtp.gmail.com
// You need to create an email app password
#define emailSenderAccount    "[email protected]"
#define emailSenderPassword   "YOUR_EMAIL_APP_PASSWORD"
#define smtpServer            "smtp.gmail.com"
#define smtpServerPort        465
#define emailSubject          "ESP32-CAM Photo Captured"
#define emailRecipient        "[email protected]"

#define CAMERA_MODEL_AI_THINKER

#if defined(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
#else
  #error "Camera model not selected"
#endif

/* The SMTP Session object used for Email sending */
SMTPSession smtp;

/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);

// Photo File Name to save in LittleFS
#define FILE_PHOTO "photo.jpg"
#define FILE_PHOTO_PATH "/photo.jpg"

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  
  Serial.begin(115200);
  Serial.println();

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  
  // Print ESP32 Local IP Address
  Serial.print("IP Address: http://");
  Serial.println(WiFi.localIP());

  // Init filesystem
  ESP_MAIL_DEFAULT_FLASH_FS.begin();
   
  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.grab_mode = CAMERA_GRAB_LATEST;
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 1;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

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

void loop() {

}

// Capture Photo and Save it to LittleFS
void capturePhotoSaveLittleFS( void ) {
  // Dispose first pictures because of bad quality
  camera_fb_t* fb = NULL;
  // Skip first 3 frames (increase/decrease number as needed).
  for (int i = 0; i < 3; i++) {
    fb = esp_camera_fb_get();
    esp_camera_fb_return(fb);
    fb = NULL;
  }
    
  // Take a new photo
  fb = NULL;  
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
  }  

  // Photo file name
  Serial.printf("Picture file name: %s\n", FILE_PHOTO_PATH);
  File file = LittleFS.open(FILE_PHOTO_PATH, FILE_WRITE);

  // Insert the data in the photo file
  if (!file) {
    Serial.println("Failed to open file in writing mode");
  }
  else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.print("The picture has been saved in ");
    Serial.print(FILE_PHOTO_PATH);
    Serial.print(" - Size: ");
    Serial.print(fb->len);
    Serial.println(" bytes");
  }
  // Close the file
  file.close();
  esp_camera_fb_return(fb);
}

void sendPhoto( void ) {
  
  /** Enable the debug via Serial port
   * none debug or 0
   * basic debug or 1
  */
  smtp.debug(1);

  /* Set the callback function to get the sending results */
  smtp.callback(smtpCallback);

  /* Declare the session config data */
  Session_Config config;
  
  /*Set the NTP config time
  For times east of the Prime Meridian use 0-12
  For times west of the Prime Meridian add 12 to the offset.
  Ex. American/Denver GMT would be -6. 6 + 12 = 18
  See https://en.wikipedia.org/wiki/Time_zone for a list of the GMT/UTC timezone offsets
  */
  config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
  config.time.gmt_offset = 0;
  config.time.day_light_offset = 1;

  /* Set the session config */
  config.server.host_name = smtpServer;
  config.server.port = smtpServerPort;
  config.login.email = emailSenderAccount;
  config.login.password = emailSenderPassword;
  config.login.user_domain = "";

  /* Declare the message class */
  SMTP_Message message;

  /* Enable the chunked data transfer with pipelining for large message if server supported */
  message.enable.chunking = true;

  /* Set the message headers */
  message.sender.name = "ESP32-CAM";
  message.sender.email = emailSenderAccount;

  message.subject = emailSubject;
  message.addRecipient("Sara", emailRecipient);

  String htmlMsg = "<h2>Photo captured with ESP32-CAM and attached in this email.</h2>";
  message.html.content = htmlMsg.c_str();
  message.html.charSet = "utf-8";
  message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp;

  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal;
  message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;

  /* The attachment data item */
  SMTP_Attachment att;

  /** Set the attachment info e.g. 
   * file name, MIME type, file path, file storage type,
   * transfer encoding and content encoding
  */
  att.descr.filename = FILE_PHOTO;
  att.descr.mime = "image/png"; 
  att.file.path = FILE_PHOTO_PATH;
  att.file.storage_type = esp_mail_file_storage_type_flash;
  att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

  /* Add attachment to the message */
  message.addAttachment(att);

  /* Connect to server with the session config */
  if (!smtp.connect(&config))
    return;

  /* Start sending the Email and close the session */
  if (!MailClient.sendMail(&smtp, &message, true))
    Serial.println("Error sending Email, " + smtp.errorReason());
}

// Callback function to get the Email sending status
void smtpCallback(SMTP_Status status){
  /* Print the current status */
  Serial.println(status.info());

  /* Print the sending result */
  if (status.success())
  {
    Serial.println("----------------");
    Serial.printf("Message sent success: %d\n", status.completedCount());
    Serial.printf("Message sent failled: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++){
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);
      time_t ts = (time_t)result.timestamp;
      localtime_r(&ts, &dt);

      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
      ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
      ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
    }
    Serial.println("----------------\n");

   // You need to clear sending result as the memory usage will grow up.
   smtp.sendingResult.clear();
  }
}

View raw code

How the Code Works

Continue reading to learn how the code works, or skip to the Demonstration section. Don’t forget to insert your network credentials and email settings in the code. Also, if you’re using a camera model other than an ESP32-CAM AI-Thinker, don’t forget to change the pin assignment.

Importing Libraries

Import the required libraries. The ESP3_Mail_Client.h is used to send emails, the FS.h is used to access and save files to LittleFS and the WiFi.h library is used to initialize Wi-Fi and connect your ESP32-CAM to your local network.

#include "esp_camera.h"
#include "SPI.h"
#include "driver/rtc_io.h"
#include <ESP_Mail_Client.h>
#include <FS.h>
#include <WiFi.h>

Network Credentials

Insert your network credentials in the following variables:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Email Settings

Type the email sender account on the emailSenderAccount variable and its password on the emailSenderPassword variable.

#define emailSenderAccount   "[email protected]"
#define emailSenderPassword  "SENDER_ACCOUNT_PASSWORD"

Insert the recipient’s email. This is the email that will receive the emails sent by the ESP32:

#define emailRecipient "[email protected]"

Insert your email provider SMTP settings on the following lines. We’re using the settings for a Gmail account. If you’re using a different email provider, replace with the corresponding SMTP settings.

#define smtpServer "smtp.gmail.com"
#define smtpServerPort 465

Write the email subject on the emailSubject variable.

#define emailSubject "ESP32-CAM Photo Captured"

Create a STMPSession object called smtp that contains the data to send via email and all the other configurations.

/* The SMTP Session object used for Email sending */
SMTPSession smtp;

The photo taken with the ESP32 camera will be temporarily saved in LittleFS under the name photo.jpg on the root directory.

#define FILE_PHOTO "photo.jpg"
#define FILE_PHOTO_PATH "/photo.jpg"

ESP32 Camera Pins

Define the pins used by your camera model. We’re using the ESP32-CAM 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

setup()

In the setup(), connect the ESP32 to Wi-Fi.

// Connect to Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println();

Print the ESP32-CAM IP address:

// Print ESP32 Local IP Address
Serial.print("IP Address: http://");
Serial.println(WiFi.localIP());

In the setup(), you initialize the filesystem using the ESP Mail Client library method. The default filesystem set in the library for the ESP32 is LittleFS (you can change the default in the library file ESP_Mail_FS.h).

// Init filesystem
ESP_MAIL_DEFAULT_FLASH_FS.begin();

The following lines configure the camera and set the camera settings:

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.grab_mode = CAMERA_GRAB_LATEST;

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;
}

Initialize the camera.

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

After initializing the camera, call the capturePhotoSaveLittleFS() and the sendPhoto() functions. These functions are defined at the end of the code.

capturePhotoSaveLittleFS() function

The capturePhotoSaveLittleFS() function captures a photo and saves it in the ESP32 LittleFS. In the following lines, you take a photo and save it in the framebuffer FB.

Note: sometimes, the first pictures taken with the ESP32-CAM are not good because the sensor has not adjusted the white balance yet. So, to make sure we get a good picture, we discard the first three pictures (that number may vary depending on your board, so adjust accordingly).

//Dispose first pictures because of bad quality
camera_fb_t* fb = NULL;
// Skip first 3 frames (increase/decrease number as needed).
for (int i = 0; i < 3; i++) {
  fb = esp_camera_fb_get();
  esp_camera_fb_return(fb);
  fb = NULL;
}
    
// Take a new photo
fb = NULL;  
fb = esp_camera_fb_get();  
if(!fb) {
  Serial.println("Camera capture failed");
  delay(1000);
  ESP.restart();
}  

Then, create a new file in LitteFS where the photo will be saved.

Serial.printf("Picture file name: %s\n", FILE_PHOTO_PATH);
File file = LittleFS.open(FILE_PHOTO_PATH, FILE_WRITE);

Check if the file was successfully created. If not, print an error message.

if (!file) {
  Serial.println("Failed to open file in writing mode");
}

If a new file was successfully created, “copy” the image from the buffer to that newly created file.

file.write(fb->buf, fb->len); // payload (image), payload length
Serial.print("The picture has been saved in ");
Serial.print(FILE_PHOTO_PATH);
Serial.print(" - Size: ");
Serial.print(fb->len);
Serial.println(" bytes");

Close the file and clear the buffer for future use.

file.close();
esp_camera_fb_return(fb);

sendPhoto() function

After having the photo successfully saved in LittleFS, we’ll send it via email by calling the sendPhoto() function. Let’s take a look at that function.

void sendPhoto( void ) {

Enable debugging via serial port for the email status:

smtp.debug(1);

Set the callback function to get the sending results:

smtp.callback(smtpCallback);

Declare an email session:

Session_Config config;

Configure the time so that the email is timestamped correctly. You may need to adjust the gmt_offset variable depending on your location.

/*Set the NTP config time
For times east of the Prime Meridian use 0-12
For times west of the Prime Meridian add 12 to the offset.
Ex. American/Denver GMT would be -6. 6 + 12 = 18
See https://en.wikipedia.org/wiki/Time_zone for a list of the GMT/UTC timezone offsets
*/
config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
config.time.gmt_offset = 0;
config.time.day_light_offset = 1;

Set the server host, port, sender email, and password for this email session:

/* Set the session config */
config.server.host_name = smtpServer;
config.server.port = smtpServerPort;
config.login.email = emailSenderAccount;
config.login.password = emailSenderPassword;
config.login.user_domain = "";

Declare a SMTP_Message class:

SMTP_Message message;

Set the message headers—sender name, email sender, subject, and recipient (you can change the recipient name):

/* Set the message headers */
message.sender.name = "ESP32-CAM";
message.sender.email = emailSenderAccount;

message.subject = emailSubject;
message.addRecipient("Sara", emailRecipient);

In the following lines, set the content of the message in the htmlMsg variable:

String htmlMsg = "<h2>Photo captured with ESP32-CAM and attached in this email.</h2>";
message.html.content = htmlMsg.c_str();
message.html.charSet = "utf-8";
message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp;

Now, we need to take care of the attachments. Create an attachment:

SMTP_Attachment att;

Set the attachment info: document name, mime type, file path, and where the content is saved (in our case it is saved in flash (esp_mail_file_storage_type_flash)):

att.descr.filename = FILE_PHOTO;
att.descr.mime = "image/png"; 
att.file.path = FILE_PHOTO_PATH;
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

Add the attachment to the message:


/* Add attachment to the message */
message.addAttachment(att);

Connect to the SMTP server:

/* Connect to server with the session config */
if (!smtp.connect(&config))
  return;

Finally, the following lines send the message:

/* Start sending the Email and close the session */
if (!MailClient.sendMail(&smtp, &message, true))
  Serial.println("Error sending Email, " + smtp.errorReason());

In this example, the email is sent once when the ESP32 boots, that’s why the loop() is empty.

void loop() {
}

To send a new email, you just need to reset your board (press the on-board RESET button).

Demonstration

After making the necessary changes to the code: camera pinout, sender’s email address, sender’s email password, recipient’s email address, and network credentials, you can upload the code to your board.

If you don’t know how to upload code to the ESP32-CAM, read the following post:

After uploading, open the Serial Monitor and press the ESP32-CAM RESET button. The ESP32 should connect to Wi-Fi, take a photo, save it in LittleFS, connect to the SMTP server, and send the email as shown below.

Send Email ESP32-CAM Serial Monitor

After a few seconds, you should have a new email from the ESP32-CAM in your inbox. As you can see in the image below, the sender’s email name is “ESP32-CAM” as we’ve defined in the code, and the subject “ESP32-CAM Photo Captured”.

ESP32 CAM Email with Photo Received Inbox

Open the email and you should see a photo captured by the ESP32-CAM.

ESP32 CAM Send Photo To Email Gmail Inbox

You can open or download the photo to see it in full size.

Photo taken with ESP32 CAM Full Size

Wrapping Up

In this tutorial, you’ve learned how to send emails with photos taken with the ESP32-CAM. The example presented is as simple as possible: it takes a photo and sends it via email when the ESP32-CAM first boots. To send another email, you need to reset the board.

This project doesn’t have a practical application, but it is useful to understand how to send an email with an attachment. After this, it should be fairly easy to include this feature in your own ESP32-CAM projects.

Other ESP32-CAM projects you may like:

Learn more about the ESP32-CAM:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

119 thoughts on “ESP32-CAM: Take and Send Photos via Email using an SMTP Server”

      • Caden,
        Thanks for your reply.
        I am not an expert in IFTTT.
        Do you know if it is possible to ‘trigger’ the reset button remote with an IFTTT command which is required to start the capturing process?

        Thanks for your help in advance.
        BR
        Arjen

        Reply
        • Arjen, I did some research on IFTTT and they sadly don’t support sending data back to the ESP32-Cam chip, such as asking it to start capturing. I found another option, which would be to set up a server with the ESP32-Cam, and have it run a Event (handleRoot) when its address (Wifi.localIP()) is opened, or “triggered”.

          The code would be:

          /*********
          Rui Santos
          Complete instructions at https://RandomNerdTutorials.com/esp32-cam-projects-ebook/

          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 “SPI.h”
          #include “driver/rtc_io.h”
          #include “ESP32_MailClient.h”
          #include <FS.h>
          #include <SPIFFS.h>
          #include <WiFi.h>
          #include <ESP8266WebServer.h>
          #include <ESP8266WiFi.h>
          #include <WiFiClient.h>

          // REPLACE WITH YOUR NETWORK CREDENTIALS
          const char* ssid = “REPLACE_WITH_YOUR_SSID”;
          const char* password = “REPLACE_WITH_YOUR_PASSWORD”;
          const char* host = “maker.ifttt.com”;
          //after you set up your account put your IFTTT key here. (see https://randomnerdtutorials.com/door-status-monitor-using-the-esp8266/)
          const char* apiKey = “YOUR_IFTTT_API_KEY”;

          // To send Email using Gmail use port 465 (SSL) and SMTP Server smtp.gmail.com
          // YOU MUST ENABLE less secure app option https://myaccount.google.com/lesssecureapps?pli=1
          #define emailSenderAccount “[email protected]
          #define emailSenderPassword “SENDER_ACCOUNT_PASSWORD”
          #define smtpServer “smtp.gmail.com”
          #define smtpServerPort 465
          #define emailSubject “ESP32-CAM Photo Captured”
          #define emailRecipient “[email protected]

          #define CAMERA_MODEL_AI_THINKER

          #if defined(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
          #else
          #error “Camera model not selected”
          #endif

          // The Email Sending data object contains config and data to send
          SMTPData smtpData;

          // Photo File Name to save in SPIFFS
          #define FILE_PHOTO “/photo.jpg”

          void handleRoot() {
          //take capture and send via email.
          capturePhotoSaveSpiffs();
          sendPhoto();

          server.send(200, “text/plain”, “took capture and sent.”);
          }

          void handleNotFound() {
          digitalWrite(led, 1);
          String message = “File Not Found\n\n”;
          message += “URI: “;
          message += server.uri();
          message += “\nMethod: “;
          message += (server.method() == HTTP_GET) ? “GET” : “POST”;
          message += “\nArguments: “;
          message += server.args();
          message += “\n”;
          for (uint8_t i = 0; i < server.args(); i++) {
          message += ” ” + server.argName(i) + “: ” + server.arg(i) + “\n”;
          }
          server.send(404, “text/plain”, message);
          digitalWrite(led, 0);
          }

          void setup() {
          WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

          Serial.begin(115200);
          Serial.println();

          // Connect to Wi-Fi
          WiFi.mode(WIFI_STA);
          WiFi.begin(ssid, password);
          Serial.print(“Connecting to WiFi…”);
          while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          Serial.print(“.”);
          }
          Serial.println();

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

          // Print ESP32 Local IP Address
          //opening this will trigger a capture
          Serial.print(“IP Address: http://“);
          Serial.println(WiFi.localIP());

          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;
          }

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

          server.on(“/”, handleRoot);

          server.onNotFound(handleNotFound);

          server.begin();
          Serial.println(“HTTP server started! Ready to take photos.”);
          }

          void loop() {
          // in the original sketch this is empty.
          //now we just keep the server up to date.
          server.handleClient();
          MDNS.update();

          }

          // Check if photo capture was successful
          bool checkPhoto( fs::FS &fs ) {
          File f_pic = fs.open( FILE_PHOTO );
          unsigned int pic_sz = f_pic.size();
          return ( pic_sz > 100 );
          }

          // Capture Photo and Save it to SPIFFS
          void capturePhotoSaveSpiffs( void ) {
          camera_fb_t * fb = NULL; // pointer
          bool ok = 0; // Boolean indicating if the picture has been taken correctly

          do {
          // Take a photo with the camera
          Serial.println(“Taking a photo…”);

          fb = esp_camera_fb_get();
          if (!fb) {
          Serial.println("Camera capture failed");
          return;
          }

          // Photo file name
          Serial.printf("Picture file name: %s\n", FILE_PHOTO);
          File file = SPIFFS.open(FILE_PHOTO, FILE_WRITE);

          // Insert the data in the photo file
          if (!file) {
          Serial.println("Failed to open file in writing mode");
          }
          else {
          file.write(fb->buf, fb->len); // payload (image), payload length
          Serial.print("The picture has been saved in ");
          Serial.print(FILE_PHOTO);
          Serial.print(" - Size: ");
          Serial.print(file.size());
          Serial.println(" bytes");
          }
          // Close the file
          file.close();
          esp_camera_fb_return(fb);

          // check if file has been correctly saved in SPIFFS
          ok = checkPhoto(SPIFFS);

          } while ( !ok );
          }

          void sendPhoto( void ) {
          // Preparing email
          Serial.println(“Sending email…”);
          // Set the SMTP Server Email host, port, account and password
          smtpData.setLogin(smtpServer, smtpServerPort, emailSenderAccount, emailSenderPassword);

          // Set the sender name and Email
          smtpData.setSender(“ESP32-CAM”, emailSenderAccount);

          // Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest)
          smtpData.setPriority(“High”);

          // Set the subject
          smtpData.setSubject(emailSubject);

          // Set the email message in HTML format
          smtpData.setMessage(”

          Photo captured with ESP32-CAM and attached in this email.

          “, true);
          // Set the email message in text format
          //smtpData.setMessage(“Photo captured with ESP32-CAM and attached in this email.”, false);

          // Add recipients, can add more than one recipient
          smtpData.addRecipient(emailRecipient);
          //smtpData.addRecipient(emailRecipient2);

          // Add attach files from SPIFFS
          smtpData.addAttachFile(FILE_PHOTO, “image/jpg”);
          // Set the storage type to attach files in your email (SPIFFS)
          smtpData.setFileStorageType(MailClientStorageType::SPIFFS);

          smtpData.setSendCallback(sendCallback);

          // Start sending Email, can be set callback function to track the status
          if (!MailClient.sendMail(smtpData))
          Serial.println(“Error sending Email, ” + MailClient.smtpErrorReason());

          // Clear all data from Email object to free memory
          smtpData.empty();
          }

          // Callback function to get the Email sending status
          void sendCallback(SendStatus msg) {
          //Print the current status
          Serial.println(msg.info());
          }

          Reply
        • Create a function that calls capturePhotoSaveSpiffs() and sendPhoto() whenever you send an IFFT trigger to it, then put this function inside main loop(). You wouldn’t need that “reset” function.

          Reply
          • sirDaniel’s idea would work if IFTTT had a way to trigger the capturePhotoSaveSpiffs() and sendPhoto() function. might be able to if you use webhooks request.

  1. These two methods are what you want to call when the web URL is triggered:

    capturePhotoSaveSpiffs();
    sendPhoto();

    you might have to create your own applet.

    The idea would be that you set up a web server, and the IFTTT applet would send info to the server when a web URL is opened.

    Reply
  2. Hi Rui and Sara,
    Great post with interesting features for other projects!
    It would be really interesting in future posts to present the code part of the post on both the arduino IDE and also MicroPython.
    Do you thing it might be possible?
    Regards
    Alain

    Reply
    • Hi.
      Thanks for your comment.
      We intend to do some projects with the ESP32-CAM and MicroPython.
      However, I don’t know when they’ll be ready.
      Regards,
      Sara

      Reply
  3. Hi,
    I tried this project and could download to the ESP.
    In the serial monitor I can see that it do all until connect to the smtp server.
    There is an error : Error, could not connect to server.
    I have activated as mentioned the lower security of the new created gmail account.
    What can I do now?
    Thank You and Best Regards
    Michael

    16:15:57.346 -> Connecting to WiFi….
    16:15:58.510 -> SPIFFS mounted successfully
    16:15:58.510 -> IP Address: http://192.168.100.242
    16:15:59.193 -> Taking a photo…
    16:15:59.501 -> Picture file name: /photo.jpg
    16:16:01.075 -> The picture has been saved in /photo.jpg – Size: 145792 bytes
    16:16:01.110 -> Sending email…
    16:16:01.110 -> Connecting to SMTP server…
    16:16:01.110 -> Error, could not connect to server
    16:16:01.110 -> Error sending Email, could not connect to server

    #define smtpServer “smtp.gmail.com”
    #define smtpServerPort 465
    #define emailSubject “ESP32-CAM Photo Captured”

    can ping the ESP in my WiFi Network and it has internet access.

    Reply
  4. WOW.. I just found this site and the many articles covering the ESP32 Cam.. So I see capture and save, capture and email, a PIR triggered send to telegram.. Has anyone done a PIR Capture, save to SD and Email yet?? I am waiting on my board, but hope to achieve this for my board..

    Reply
  5. hello, I want to thank you for your work.
    I would like to know if we can add an HC-SR501 PIR Motion Sensor Module as for telegram.
    Thank you for your reply

    Reply
  6. hi there
    Thanks for great instructions !!
    At the end, after reset the board, on my COM4 monitor I always(!) receive the same:

    d⸮⸮⸮⸮⸮888 g⸮{⸮⸮s⸮⸮KG

    May be someone can help… Thanks !

    Reply
  7. I always get this error in the serial terminal:

    [E][camera.c:1483] esp_camera_fb_get(): Failed to get the frame on time!

    And I receive the email with no photo attached.

    I think the problem is not the code because I have tried with the Web server example and that error happens too sometimes. I think the problem is any library… I have no idea. After searching on the Internet, it seems that problem is common (other people reported) but no clear instructions about how to solve it.

    I wonder if the SD card is the problem… And the camera is unable to store the photo.

    Reply
    • I found out the solution for my case, I hope many others can read this message because I have seen many people frustrated on the Internet with the error ” esp_camera_fb_get(): Failed to get the frame on time!”.

      Everything was solved just changing some parameters:


      config.xclk_freq_hz = 5000000;

      config.frame_size = FRAMESIZE_SVGA;

      If other people found other solution it worth sharing. Because I have seen it is a common issue.

      Reply
  8. I can’t make it work. I always get

    Connecting to SMTP server…
    Error, could not connect to server
    Error sending Email, could not connect to server

    All the credentials are properly set and I can send emails using the same credentials with other programs. What could be wrong?

    Many thanks in advance for any hint.

    Reply
      • Hi Sara, “muito obrigado” for your prompt answer, yes I’m using a Gmail account with Allow less secure apps ON, and I have ESP32 mail client V2.1.1 installed. I’ve tried with V2.1.6 but it failed exactly the same as before.

        Help me please! I really need to be able to send a picture from the ESPcam! (my application is for construction tracking)

        Thanks

        Reply
          • Olá Sara, yes it worked like a charm!

            So it seems this other library works better with Gmail, unfortunately I’m not that familiar with Arduino as to integrate both examples (the picture taking from the first example and the email part from the second) and would appreciate if you could assist on that. After all it will be a great update to your excellent tutorial.

            Obrigado e abraço!

          • Hi.
            I just tested the example and it works for me. I have the ESP32 Mail Client Library version 1.3.0 and ESP32 boards version 1.0.6 (you can check it in Tools > Boards > Boards Manager and search for ESP32).
            However, the ESP32 Mail Client Library is deprecated in favor of the ESPMailClient.
            So, here’s the example using the ESPMAilClient library instead: https://gist.github.com/sarasantos/30b520472b5f33a1af048b25b1798d12
            I didn’t update the tutorial yet because I need to clean up the code a bit before posting, but that link that I’ve sent you is working. I’ve tested it.
            I hope this helped.
            Regards,
            Sara

  9. Sara, I’m sorry but I’m still having problems 🙁

    My board version is the same as yours: 1.0.6

    ESP32 Mail Client Library I wasn’t able to find the same version you have (1.3.0) on the Library Managements it jumps from 1.2.3 to 2.0.0. Could you please double check your version?

    And using ESP Mail Client V1.3.0 running the last example it gives me the same error

    The picture has been saved in /photo.jpg – Size: 75648 bytes
    Connecting to SMTP server…

    C: ESP Mail Client v1.3.0
    C: connect to SMTP server
    C: host > smtp.gmail.com
    C: port > 465
    C: starting socket
    ! E: could not get ip from host
    C: cleaning SSL connection
    Error, unable to connect to server
    E: unable to connect to server

    I’m not sure what am I doing wrong…

    Reply
      • Dear Sara, I really appreciate your attention to help me.

        So I’ve exactly the same versions as you but in my case it works OK with the example with no picture attached and it can’t connect to the mail server when trying to send the picture.

        What else could I try? What could I have wrong?

        I have been fighting with this for two days now 🙁

        Reply
        • Sara, just one additional info that might help, I’ve tried with a different email service provider (UOL a very popular local service) and the issue was exactly the same

          The picture has been saved in /photo.jpg – Size: 156544 bytes
          Connecting to SMTP server…

          C: ESP Mail Client v1.3.0
          C: connect to SMTP server
          C: host > smtps.uol.com.br
          C: port > 465
          C: starting socket
          C: connecting to Server
          ! E: connect to Server failed!
          C: cleaning SSL connection
          Error, unable to connect to server
          E: unable to connect to server

          Reply
          • I’m sorry, I don’t know what else I can do to solve your issue.
            The project is working for me with the conditions I mentioned previously.
            Without further details, it is very difficult to find out what might be wrong.
            Regards,
            Sara

          • Sara, just as last resource could you please share with me your settings for the
            Board’s CPU Frequency and Flash Frequency ??

            May be there is something there, just guessing…

            Thanks

          • Hi.
            I don’t think that is relevant for the issue.
            But, here are my settings:
            CPU frequency: 240MHz (WiFi/BT)
            Flash Frequency: 80 MHz
            Regards,
            Sara

          • Hi Sara, just to share that my ESPcam was indeed with some kind of problem (very strange by the way) and the new board is working properly.

            Many thanks again for your help.

            Abraço

  10. Thanks Sara, as you expected it made no difference…

    I have ordered a new board because there is nothing else I can think of. Will report back if it works.

    Best

    Reply
    • ok.
      Thanks for keeping me updated.
      It can be a bad wi-fi connection. Try putting the board closer to your router.
      Regards,
      Sara

      Reply
  11. Hi Sara & Rui
    Your project is very cool.
    I really appreciate what you are doing.

    I am more and more drawn to this project and I am wondering if it is possible (and how) to make esp32-cam send a photo automatically every 24 hours?

    Thank you for your help
    Regards Adam

    Reply
  12. Hallo, kurze Anfrage: beim hochladen des codes wird mir folgender Fehler angezeigt:
    ……espressif\esp32/tools/esptool/esptool.exe”: file not exist
    Fehler beim Kompilieren für das Board AI Thinker ESP32-Cam

    Was muß ich tun ?

    LG
    XXAL

    Reply
  13. Hi,
    The email is sent OK with the pic attached but it keeps sending the same picture not an updated one. I want to send an updated pic continuously every half hour.

    All I changed/added to your code is:

    // capturePhotoSaveSpiffs();
    // sendPhoto();
    }

    void loop() {
    capturePhotoSaveSpiffs();
    sendPhoto();
    delay (1800000);
    }

    Thanks

    Reply
      • This does what I want. It send a pic every half hour if bright enough. Sends the updated pic then removes it from SPIFFS.
        It was sending only a partial or no pic so also added a SPIFFS format statement in the setup to correct this.

        bool formatted = SPIFFS.format(); //formats SPIFFS if corrupt and only partial picture

        void loop() {

        if (digitalRead(2) == LOW) digitalWrite(33, LOW); // TURN on LED if dark

        if (digitalRead(2) == HIGH) {
        digitalWrite(33, HIGH);

        delay (100);
        capturePhotoSaveSpiffs();
        delay (300);
        sendPhoto();
        SPIFFS.remove("FILE_PHOTO");
        delay (1800000);

        }

        Reply
  14. Hello there.

    I am keep taking this error

    sketch_feb14a:12:30: fatal error: ESP32_MailClient.h: No such file or directory
    compilation terminated.
    exit status 1
    ESP32_MailClient.h: No such file or directory

    I am trying to include ESP32_MailClient.h but it does nothing.
    does it happened to anyone else ?

    Thanks

    Reply
    • Hi.
      Are you sure you’re installing the right library?
      There are libraries with similar names that are not compatible.
      Regards,
      Sara

      Reply
      • Hello Sara.
        Thanks for the Reply.

        I searched on Library Manager for ESP 32 Mail client Libraries.
        I found two (2) libraries:
        ESP Mail Client (Version 2.0.3)
        ESP32 Mail Client. (Version 2.1.6)
        I installed both of them to be sure. but nothing happened.

        Just to be sure. I changed the ” ” with < > in the include. But the error is still there.

        If you have any idea of what it could be please inform me.

        Thanks for your time.

        Reply
        • Remove the ESP Mail Client library, and only use the ESP32 Mail Client.
          I’m not sure what might be causing the issue.
          Regards,
          Sara

          Reply
  15. Error, login password is not valid
    Error sending Email, login password is not valid

    Guess this is Google’s issue. It used to work properly when I completed the project. The ESP32 CAM was able to send photos and login without any issues. Thanks to Google, it’s no longer working. I can’t think of any other solutions other than trying other email providers.

    Reply
    • Hi.
      Double-check your credentials.
      And make sure you allow less secure apps in your account settings.
      Regards,
      Sara

      Reply
      • Hello Sara, thanks for the reply. Just to update, I found a solution for the issue I was facing not too long after posting the comment.

        Basically, I turned on 2 factor authentication and enabled something called “App Passwords”. Next, I generated a 16 pin password with app password and used that in the login credentials field inside the code. The project is able to send email again after doing those.

        I would recommend updating the steps for this tutorial. This is because by May 30 2022, ​​Google will no longer support the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password. Because of that, 2 factor and app password has to be used.

        Sorry for the late reply as I was busy and forgotten about replying.

        Link for screenshot reference regarding app password: https://drive.google.com/file/d/1mufB6QZtUB731LHnX2S4LONryEU30Bmb/view?usp=sharing

        Reply
        • Hi.
          Thanks for sharing.
          I’ll test that and update the tutorials that use gmail.
          Thank you so much.
          Regards,
          Sara

          Reply
  16. Great Project. I have added TTGO camera configuration and Sleep Mode:
    I thought I would share but excluded my credentials.

    /*
    // Pin definitions for My ESP32-CAM
    #define 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

    */

    // Pin definitions for CAMERA_MODEL_TTGO_WHITE (Mine)
    #define CAMERA_MODEL_TTGO_WHITE_BME280
    #define PWDN_GPIO_NUM 26
    #define RESET_GPIO_NUM -1
    #define XCLK_GPIO_NUM 32
    #define SIOD_GPIO_NUM 13
    #define SIOC_GPIO_NUM 12
    #define Y9_GPIO_NUM 39
    #define Y8_GPIO_NUM 36
    #define Y7_GPIO_NUM 23
    #define Y6_GPIO_NUM 18
    #define Y5_GPIO_NUM 15
    #define Y4_GPIO_NUM 4
    #define Y3_GPIO_NUM 14
    #define Y2_GPIO_NUM 5
    #define VSYNC_GPIO_NUM 27
    #define HREF_GPIO_NUM 25
    #define PCLK_GPIO_NUM 19

    // Photo File Name to save in SPIFFS
    #define PhotoFileName “/photo.jpg”

    String Filename() {
    return String(FILE).substring(String(FILE).lastIndexOf(“\”) + 1);
    }

    void setup() {
    Serial.begin(115200);
    delay(500);
    Serial.println(“\nProgram ~ ” + Filename());
    Serial.println(“Date Compiled ~ ” + String(DATE) + “\n”);

    // CONNECT to WiFi network:
    Serial.print(“WiFi Connecting to ” + String(ssid) + ” “);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(300);
    Serial.print(“.”);
    }
    Serial.printf(” ~ Connected OK\n”);

    // Print the Signal Strength:
    long rssi = WiFi.RSSI() + 100;
    Serial.print(“Signal Strength = ” + String(rssi));
    if (rssi > 50) Serial.printf(” (>50 – Good)\n”); else Serial.printf(” (Could be Better)\n”);

    Serial.setDebugOutput(true);

    // Set Up SPIFFS
    SPIFFS.begin();
    if (!SPIFFS.begin(true)) {
    Serial.printf(“An Error has occurred while Mounting SPIFFS\n”);
    while (1) {
    delay(1000); // Stay here
    } else {
    Serial.printf(” ~ SPIFFS Mounted Successfully\n”);
    }
    }
    // Configure Camera Settings
    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;

    //Initial setting are FRAMESIZE_UXGA (320×240), Reset to SVGA (800×600)
    //Initalize with high specs to pre-allocate larger buffers
    if (psramFound()) {
    Serial.printf(” ~ PSRAM Found OK! Frame Set to SVGA\n”);
    config.frame_size = FRAMESIZE_SVGA; // (800×600)
    config.jpeg_quality = 10; // 10-63 lower number means higher quality
    config.fb_count = 2;
    } else {
    Serial.printf(“NO PSRAM found, Check Board Configuration !! Frame Set to UXGA\n”);
    config.frame_size = FRAMESIZE_UXGA; // (320×240)
    config.jpeg_quality = 12;
    config.fb_count = 2;
    }

    // Initialize Camera
    esp_err_t err = esp_camera_init(&config);
    if (err != ESP_OK) {
    if (err == 0x105) Serial.printf(“Check Camera Pins Configuration !!\n”);
    Serial.printf(“Camera init failed with error 0x%x”, err);
    return;
    }

    sensor_t * s = esp_camera_sensor_get();
    //Initial Image is flipped vertically/horzontally and colors are a bit saturated
    //s->set_vflip(s, 1); // Flip it back
    //s->set_brightness(s, 1); // Up the blightness just a bit
    //s->set_saturation(s, -2); // Lower the saturation

    if (s->id.PID == OV2640_PID) Serial.printf(” ~ Camera is OV2640\n”);
    if (s->id.PID == OV3660_PID) Serial.printf(” ~ Camera is OV3660\n”);
    if (s->id.PID == OV5640_PID) Serial.printf(” ~ Camera is OV5640\n”);

    Serial.printf(” ~ Camera Set Up OK!\n”);

    Serial.printf(“*******************************************\n”);

    Capture_Photo_Save_Spiffs();

    //Send_Photo(); // Moved “Send_Photo” at end of “Capture_Photo_Save_Spiffs”

    //Print the wakeup reason for ESP32
    Print_Wakeup_Reason();

    // EXTERNAL TRIGGER ~ GPIO_NUM_13 and Input High for ESP32-CAM
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1); // 1 = High, 0 = Low

    // EXTERNAL TRIGGER ~ GPIO_NUM_33 and Input High for TTGO
    if (PWDN_GPIO_NUM == 26) { // for TTGO
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // for TTGO
    }
    //If you were to use ext1, you would use it like:
    //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

    // TIMED TRIGGER ~ First we configure the wake up source
    // We set our ESP32 to wake up every so many miliseconds (60 seconds) ~ TIMED TRIGGER
    //esp_sleep_enable_timer_wakeup(1000000 * 60));

    Serial.printf(“Going to Sleep now!\n”);
    Serial.flush();
    esp_deep_sleep_start();
    }

    void loop() {
    // Nothing to do here!!
    }

    void Print_Wakeup_Reason() {
    esp_sleep_wakeup_cause_t wakeup_reason;
    wakeup_reason = esp_sleep_get_wakeup_cause();
    Serial.printf(“\n”);
    switch (wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.printf(“Wakeup caused by EXTERNAL Signal using RTC_IO\n”); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.printf(“Wakeup caused by EXTERNAL Signal using RTC_CNTL\n”); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.printf(“Wakeup caused by TIMER\n”); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.printf(“Wakeup caused by Touchpad\n”); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println(“Wakeup caused by ULP program\n”); break;
    default : Serial.printf(“Wakeup was Not Caused by Deep Sleep: %d\n”, wakeup_reason); break;
    }
    }

    // Capture Photo and Save it to SPIFFS
    void Capture_Photo_Save_Spiffs( void ) {
    camera_fb_t * fb = NULL; // pointer
    bool ok = 0; // Boolean indicating if the picture has been taken correctly

    do {
    // Take a photo with the camera
    //fb = esp_camera_fb_get();
    camera_fb_t * fb = esp_camera_fb_get();
    if (!fb) {
    Serial.printf(“Camera capture FAILED\n”);
    return;
    }
    // Write data to the photo file
    File file = SPIFFS.open(PhotoFileName, “w+”); // Changed to “w+”
    // Check file
    if (!file) {
    Serial.printf(“FAILED to open file in writing mode\n”);
    return;
    } else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    }
    // Close the file
    Serial.printf(“Successfully Saved as \t’%s’ – Size: %d bytes\n”, PhotoFileName, file.size());
    file.close();
    // Return the frame buffer back to the driver for reuse
    esp_camera_fb_return(fb);

    // check if file has been correctly saved in SPIFFS
    ok = Check_Photo(SPIFFS);

    } while ( !ok );
    Send_Photo();
    }

    // Check if photo capture was successful
    bool Check_Photo( fs::FS &fs ) {
    File f_pic = fs.open( PhotoFileName );
    unsigned int pic_sz = f_pic.size();
    Serial.printf(“Photo Verified as \t’%s’ – Size: %d bytes\n”, PhotoFileName, pic_sz);
    return ( pic_sz > 100 );
    }

    void Send_Photo( void ) {
    // Preparing email
    // Set the SMTP Server Email host, port, account and password
    smtpData.setLogin(smtpServer, smtpServerPort, emailSenderAccount, emailSenderPassword);

    // Set the sender name and Email
    smtpData.setSender(“ESP32-CAM”, emailSenderAccount);

    // Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest)
    smtpData.setPriority(“High”);

    // Set the subject
    smtpData.setSubject(emailSubject);

    // Set the email message in HTML format
    smtpData.setMessage(”

    Photo captured with ESP32-CAM and attached in this email.

    “, true);
    // Set the email message in text format
    //smtpData.setMessage(“Photo captured with ESP32-CAM and attached in this email.”, false);

    // Add recipients, can add more than one recipient
    smtpData.addRecipient(emailRecipient);
    //smtpData.addRecipient(emailRecipient2);

    // Add attach files from SPIFFS
    smtpData.addAttachFile(PhotoFileName, “image/jpg”);
    // Set the storage type to attach files in your email (SPIFFS)
    smtpData.setFileStorageType(MailClientStorageType::SPIFFS);

    smtpData.setSendCallback(sendCallback);

    // Start sending Email, can be set callback function to track the status
    if (!MailClient.sendMail(smtpData))
    Serial.printf(“Error sending Email %s\n”, MailClient.smtpErrorReason());

    // Clear all data from Email object to free memory
    smtpData.empty();
    }

    // Callback function to get the Email sending status
    void sendCallback(SendStatus msg) {
    //Print the current status
    Serial.println(msg.info());
    }

    Reply
  17. Hi,

    I’ve got a TTGO T-Camera (the white version, with a PIR) which won’t wake from deep sleep
    when powered via a 3.7V LiPo battery attached to the on-board battery connector.

    I have modified this sketch where the Camera goes to Sleep and then Wakes Up, either when PIR is triggered or after a wait period.

    Sleep/Wake behaves completely normally when powered via USB.

    Interestingly, I’ve noticed that when initially connecting the battery without USB power, the board also won’t power on until I’ve pressed RST.

    But with the battery attached and not powered via USB:
    If I let it sleep, then trigger a wake within a few seconds, it works.
    If I let it sleep, leave it for ~10 seconds or so, it will NOT Wake up again.

    Does anyone know how to fix this this issue?
    I tried adding:
    rtc_gpio_init(GPIO_NUM_14);
    rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(GPIO_NUM_14, 1);
    delay(500);
    But it doesn’t work.
    Any advice greatly appreciated!

    Reply
  18. #include “ESP32_MailClient.h” has been changed to #include <ESP_Mail_Client.h>
    I tried to use the first “include” statement and it comes up “not found”. Note the underscores between ESP and Mail, and Mail and Client.

    Reply
  19. Helo !
    Your’s tutorial is great. 🙂

    I have a problem.

    In code there is
    “define FILE_PHOTO “/photo.jpg”
    and
    #define emailSubject “Subkect”

    My question is, how can I set this names like this “/Photo_2022-06-05_13:00.jpg” or subject as “Subject _2022-06-05_13:00”
    Data and time is from actual time.

    Thanks for any help.
    All The Best

    Reply
  20. Hi.
    I wrote the program I wrote about earlier. However, I have a problem.
    ESP23-cam takes pictures of the gas meter 9 times a day. E.g. 23:59:00, 00:00:00 00:01:00 then 02:59:00, 03:00:00 then 05: 59: 00.06: 00: 00, 06:01:00 and so on . Why, so many photos for 1 hour – to be sure that one of the three will be correct.

    It happens that it takes 3 photos correctly, and sometimes only one. Sometimes the digits are half visible on one of them due to lack of light from backlight.
    Below come of the code in which the procedure is called take a photo

    // set of commands for each hour
    const char * Time_foto0 = “23:59:00”;
    const char * Time_foto01 = “00:00:00”;
    const char * Time_foto02 = “00:01:00”;
    e.t.c

    void loop(){
    if (strcmp (Time_, Time_foto0) == 0)
    digitalWrite (12, HIGH); // turn on the backlight
    capturePhotoSaveSpiffs ();
    digitalWrite (12, LOW); // turn off the backlight
    sendPhoto ();
    }
    if (strcmp (Time_, Time_foto01) == 0)
    digitalWrite (12, HIGH); // turn on the backlight
    capturePhotoSaveSpiffs ();
    digitalWrite (12, LOW); // turn off the backlight
    sendPhoto ();
    }
    e.t.c

    }

    Each fixed time triggers a set of commands. ESP takes a picture and sends it to gmail.
    Below are the settings and procedures
    Camera:
    void setup()
    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.xclk_freq_hz = 11000000;
    config.pixel_format = PIXFORMAT_JPEG;

    if (psramFound ()) {
    config.frame_size = FRAMESIZE_XGA; // UXGA; // UXGA;
    config.jpeg_quality = 7;
    config.fb_count = 2;
    } else {
    config.frame_size = FRAMESIZE_XGA; // SXGA;
    config.jpeg_quality = 77;
    config.fb_count = 1;
    }
    // Initialize camera
    esp_err_t err = esp_camera_init (& config);
    if (err! = ESP_OK) {
    Serial.printf (“Camera init failed with error 0x% x”, err);
    return;
    }
    sensor_t * s = esp_camera_sensor_get ();
    s-> set_brightness (s, 0); // -2 is 2
    s-> set_contrast (s, 2); // -2 is 2
    s-> set_saturation (s, 0); // -2 is 2
    s-> set_special_effect (s, 0); // 0 to 6 (0 – No Effect, 1 – Negative, 2 – Grayscale, 3 – Red Tint, 4 – Green Tint, 5 – Blue Tint, 6 – Sepia)
    s-> set_whitebal (s, 1); // 0 = disable, 1 = enable
    s-> set_awb_gain (s, 1); // 0 = disable, 1 = enable
    s-> set_wb_mode (s, 0); // 0 to 4 – if awb_gain enabled (0 – Auto, 1 – Sunny, 2 – Cloudy, 3 – Office, 4 – Home)
    s-> set_exposure_ctrl (s, 1); // 0 = disable, 1 = enable
    s-> set_aec2 (s, 0); // 0 = disable, 1 = enable
    s-> set_ae_level (s, -1); // -2 is 2
    s-> set_aec_value (s, 300); // 0 to 1200
    s-> set_gain_ctrl (s, 1); // 0 = disable, 1 = enable
    s-> set_agc_gain (s, 0); // 0 to 30
    s-> set_gainceiling (s, (gainceiling_t) 0); // 0 is 6
    s-> set_bpc (s, 0); // 0 = disable, 1 = enable
    s-> set_wpc (s, 1); // 0 = disable, 1 = enable
    s-> set_raw_gma (s, 1); // 0 = disable, 1 = enable
    s-> set_lenc (s, 1); // 0 = disable, 1 = enable
    s-> set_hmirror (s, 0); // 0 = disable, 1 = enable
    s-> set_vflip (s, 1); // 0 = disable, 1 = enable
    s-> set_dcw (s, 1); // 0 = disable, 1 = enable
    s-> set_colorbar (s, 0); // 0 = disable, 1 = enable
    }

    // Check if photo capture was successful
    bool checkPhoto (fs :: FS & fs) {
    File f_pic = fs.open (“/” + FILE_PHOTO);
    unsigned int pic_sz = f_pic.size ();
    return (pic_sz> ​​100);
    }

    // Capture Photo and Save it to SPIFFS
    void capturePhotoSaveSpiffs (void) {
    camera_fb_t * fb = NULL; // pointer
    bool ok = 0; // Boolean indicating if the picture has been taken correctly

    to {
    // Take a photo with the camera
    Serial.println (“I’m taking a photo …”);

    delay (200);
    // Take a picture
    fb = esp_camera_fb_get ();
    // After doing
    if (! fb) {
    Serial.println ("Camera capture failed");
    return;
    }

    // Determine the file name
    Serial.printf ("Filename:", FILE_PHOTO);
    File file = SPIFFS.open ("/" + FILE_PHOTO, FILE_WRITE);

    // Put the data from the photo into the file
    if (! file) {
    Serial.println ("Problem with switching to writing mode");
    }
    else {
    file.write (fb-> buf, fb-> len); // payload (image), payload length
    Serial.print ("Picture Saved In");
    Serial.print ("/" + FILE_PHOTO);
    Serial.print ("- Size:");
    Serial.print (file.size ());
    Serial.println ("bytes");
    }
    // Close the file
    file.close ();
    esp_camera_fb_return (fb);
    // see if the file is saved correctly

    Is this config.xclk_freq_hz = 11000000; problem ?

    Thanks for help
    All The Best 4U

    Reply
  21. Hia, while the project ESP32-cam Take a poto and save on SD card (https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card) works absolutely fine, when I upload and try to run this one, I get camera not found error –

    20:34:09.721 -> E (10672) camera: Camera probe failed with error 0x105(ESP_ERR_NOT_FOUND)
    20:34:09.721 -> Camera init failed with error 0x105

    With the exact same module working fine with another script, how do I solve this? I am powering it with 5V from my PC, via the ESP32-CAM-MB micro usb programmer.

    Reply
  22. Hi Sara,

    Thanks for the sketch “Using ESP32Cam to send photo to email. Worked first time around.
    I have a motion sensor and want to use it to send an email everytime motion is detected.
    How should I modify the sketch?

    Meir

    Reply
  23. Hello, good time. I can’t send email anymore with esp32cam because google settings to receive programs with less security will be removed. Please help.

    Reply
    • You need to create an app password.
      See the section of the tutorial with the title “Create an App Password”.
      Regards,
      Sara

      Reply
  24. How to fix this problem ???

    Arduino: 1.8.19 (Windows 10), Board: “ESP32 Wrover Module, Face Recognition (26211440 bytes), QIO, 80MHz, 921600, None”

    C:\Users\Lenovo-PC\OneDrive\Documents\Arduino\libraries\SPI\src\SPI.cpp: In member function ‘void SPIClass::transferBytes(const uint8_t*, uint8_t*, uint32_t)’:

    C:\Users\Lenovo-PC\OneDrive\Documents\Arduino\libraries\SPI\src\SPI.cpp:297:43: error: invalid conversion from ‘const uint8_t* {aka const unsigned char}’ to ‘uint8_t {aka unsigned char*}’ [-fpermissive]
    spiTransferBytes(_spi, data, out, size);
    ^
    In file included from C:\Users\Lenovo-PC\OneDrive\Documents\Arduino\libraries\SPI\src\SPI.h:26:0,

    from C:\Users\Lenovo-PC\OneDrive\Documents\Arduino\libraries\SPI\src\SPI.cpp:22:

    C:\Users\Lenovo-PC\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\cores\esp32/esp32-hal-spi.h:108:6: note: initializing argument 2 of ‘void spiTransferBytes(spi_t*, uint8_t*, uint8_t*, uint32_t)’

    void spiTransferBytes(spi_t * spi, uint8_t * data, uint8_t * out, uint32_t size);

    ^

    exit status 1

    Error compiling for board ESP32 Wrover Module.

    This report would have more information with
    “Show verbose output during compilation”
    option enabled in File -> Preferences.

    Reply
  25. Thank you for the work you have put into this tutorial.

    Just curious … I notice that there are often multiple attempts at taking the image. The software handles this but what is the reason for this behaviour?

    Dave

    Reply
  26. Hello and Congratulations for your excellent work!
    As a beginner to AI-Thinker ESP32, I have faced a problem with this project. Everything works fine (from connecting to WiFi to sending photo). However, the photo taken is too dark (most of the times black). I’ve found relevant code to make changes to the camera settings but with no result. Is there anything I can do or search to solve this.
    Thank you !

    Reply
    • Hi.
      We’ll update this tutorial soon with some fixes that need to be done because of the updates of the ESP32 core.
      With the new updates of the ESP32 core, when you get a frame buffer with fb_get(), it saves four different pictures in the buffer. Aditionally, the first picture always comes with a green tint.
      How we resolve this issue:
      – set config.grab_mode = CAMERA_GRAB_LATEST; to show the latest picture in the buffer
      – set config.fb_count = 1; to get only one picture in the buffer.
      – to ignore the first picture, we added the following when taking a picture – basically getting the latest picture, clearing the buffer and taking a new picture:
      – // Take a photo with the camera
      fb = esp_camera_fb_get();
      esp_camera_fb_return(fb); // dispose the buffered image
      fb = NULL; // reset to capture errors
      // Get fresh image
      Serial.println(“Taking a photo…”);
      fb = esp_camera_fb_get();
      if (!fb) {
      Serial.println(“Camera capture failed”);
      return;
      }

      I hope this helps.
      Regards,
      Sara

      Reply
  27. After using this code for several months I tried to recompile it for a new camera and I get an error saying No module found “serial”
    Any ideas on how to fix this?
    The original camera and code have been working fine.
    Thank You

    Reply
  28. Hello,
    I really appreciate what you are doing.
    As a beginner to AI-Thinker ESP32, I have faced a problem with this project.
    I always get this error in the serial terminal:

    E (1889) cam_hal: cam_dma_config(306): frame buffer malloc failed
    E (1889) cam_hal: cam_config(390): cam_dma_config failed
    E (1889) camera: Camera config failed with error 0xffffffff
    Camera init failed with error 0xffffffff

    Is there anything I can do or search to solve this.
    Thank you.

    Reply
    • Hi.
      check the connections between the camera and the board.
      check the power supply.
      The camera may also be broken.
      Regards,
      Sara

      Reply
  29. I have the following result:
    ..
    SPIFFS mounted successfully
    IP Address: http://192.168.1.95
    Taking a photo…
    Picture file name: /photo.jpg
    cam_hal: EV-EOF-OVF
    cam_hal: EV-EOF-OVF
    cam_hal: EV-EOF-OVF
    cam_hal: EV-EOF-OVF
    cam_hal: EV-EOF-OVF
    cam_hal: EV-EOF-OVF
    The picture has been saved in /photo.jpg – Size: 36864 bytes
    Sending email…
    Connecting to SMTP server…
    [ 9189][I][ssl_client32.cpp:292] start_ssl_client(): WARNING: Use certificates for a more secure communication!
    SMTP server connected, wait for response…
    Identification…
    Authentication…
    Sign in…
    Sending Email header…
    Sending Email body…
    Sending attachments…
    [ 11859][W][SPIFFS.cpp:71] begin(): SPIFFS Already Mounted!
    /photo.jpg
    Finalize…
    Finished
    Email sent successfully

    When i check my email i will get an email with an attachment but the photo is completely black,
    Any idea why ?

    Reply
  30. It seems when you call capturePhotoSaveSpiffs two times, for example from the loop by a PIR sensor trigger, the second time the FILE_PHOTO has lenght zero.
    To make it work i added a flush and a read file and this solved this for me:

    else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.println(“flush the data”);
    file.flush();
    Serial.println(“flush done, now do FILE_READ”);
    file = SPIFFS.open(FILE_PHOTO, FILE_READ);
    Serial.println(“FILE_READ done”);
    delay(1000);
    Serial.print(“The picture has been saved in “);
    Serial.print(FILE_PHOTO);
    Serial.print(” – Size: “);
    Serial.print(file.size());
    Serial.println(” bytes”);

    Reply
  31. Another issue is this statement:
    smtpData.setFileStorageType(MailClientStorageType::SPIFFS);

    I wanted to change SPIFFS in LittleFS but it does not support LittleFS,
    only SPIFFS and SD are supported .
    I guess LittleFS is too new 🙂
    If you guys know a better EMAIL send code with LittleFS, I would be interested.

    Regards
    Otto

    Reply
  32. My best kudos for your easy to understand and easy to test tutorials.
    Now I’m trying to send photos via e-mail and I probably missed some steps because I get these messages:

    19:34:30.386 -> ! E: network connection callback is required
    19:34:30.386 -> ! E: network connection status callback is required
    19:34:30.386 -> #### Error, client and/or necessary callback functions are not yet assigned
    19:34:30.386 -> ! E: client and/or necessary callback functions are not yet assigned

    can you help me to solve this problem?

    Thanks and still good.

    Reply
    • Hi.
      Did you copy the whole code?
      I think you’re missing the functions definitions ate the end of the code.
      Or if you’re using PlatformIO, you need to move the function definitions before the loop() and setup().
      Regards,
      Sara

      Reply
      • Hello and thanks for answering me.
        I downloaded the code again and compared with the one that gives me problems.
        I added :

        #define FORMAT_LITTLEFS_IF_FAILED true

        and inside setup :

        #ifdef TWOPART
        if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, “/lfs2”, 5, “part2”)){
        Serial.println(“part2 Mount Failed”);
        return;
        }
        appendFile(LittleFS, “/hello0.txt”, “World0!\r\n”);
        readFile(LittleFS, “/hello0.txt”);
        LittleFS.end();
        Serial.println( “Done with part2, work with the first lfs partition…” );
        #endif
        if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
        Serial.println(“LittleFS Mount Failed”);
        return;
        }

        The code is otherwise identical to your example.
        Can you have any other ideas about this?
        Thanks for the kind cooperation.

        Reply
  33. Hi Sarah,

    Same problem, identical example previous SMTP and Camera examples work. Same error! See config is a local varialble shared by Camera and SMTP but they are local,,,

    Reply
  34. Hi Sara,
    can you help me with this error?

    19:34:30.386 -> ! E: network connection callback is required
    19:34:30.386 -> ! E: network connection status callback is required
    19:34:30.386 -> #### Error, client and/or necessary callback functions are not yet assigned
    19:34:30.386 -> ! E: client and/or necessary callback functions are not yet assigned

    thank you.

    Reply
    • Hello sir, I have already added the the Gmail App password but I am still unable to connect to the SMTP server and send the picture. It says the error:

      10:27:47.019 -> < S: 535-5.7.8 Username and Password not accepted. For more information, go to
      10:27:47.020 -> < S: 535 5.7.8 https://support.google.com/mail/?p=BadCredentials u26-20020a63235a000000b005c2420fb198sm15596455pgm.37 – gsmtp
      10:27:47.020 -> #### Error, authentication failed
      10:27:47.020 -> ! E: authentication failed

      Reply
      • Hi.
        As the error mentions, it’s an authentication problem.
        Double-check your app password and email.
        Remember that uppercase and lowercase matters and remove any spaces.
        Regards,
        Sara

        Reply
  35. Hi,
    To whom it may concern;-)
    I had some problems to make everything functioning correctly.
    Everything looked OK but no picture was sent per e-mail.
    I found out that software on mail-servers may function differently.
    I changed:
    message.enable.chunking = true;
    in:
    message.enable.chunking = false;
    That worked on the mailserver I use.

    Regards,
    André

    Reply
  36. The above seems to have an issue sending the actual image in my testing, it seems as if it tries to send it, but takes too long and eventually sends a notice of failure

    Reply
      • Hi,
        After tweaking and testing I have found a low rate of success, I have however been capable of receiving a couple of successful emails. Most of the time the software runs until the part where it tries to send the image and then it fails. Every so often it will also have issues connecting to the smtp server

        Reply
    • Hi.
      Make sure you’re using the app password and not the email password.
      Check the section “Create an App Password”
      Regards,
      Sara

      Reply
  37. Hi Sara,

    I’m loving the projects you have on RNT and have worked through several already with good results. I have a question regarding the photo to email sketch(which I have running). Can I manipulate the code in this sketch without causing errors, so that the picture is taken first before the wifi connects and then sends the picture? This is to enable a trigger from an Arduino board to take the picture in the quickest time possible.

    Thanks & regards,
    Steve

    Reply
  38. Hi there,
    thanks for the code. Just Awesome <3

    But I get this output in the serial Monitor whenever I try to run it:
    Connecting to WiFi…..
    IP Address: http://192.168.0.189
    E (1631) esp_littlefs: ./components/esp_littlefs/src/littlefs/lfs.c:1225:error: Corrupted dir pair at {0x0, 0x1}

    E (1632) esp_littlefs: mount failed, (-84)
    E (1636) esp_littlefs: Failed to initialize LittleFS
    E (1674) camera: Camera probe failed with error 0x105(ESP_ERR_NOT_FOUND)
    Camera init failed with error 0x105

    Can you help ?

    Reply
  39. According to my personal tests it sometimes works, but most of the time not because either the ntp servers do not respond (timeout) or the connection to the SMTP server fails. Without changing anything, just unplugging and reconnecting, without even touching the code, it works… sometimes.

    Reply
  40. Nice and simple. Thanks a lot. I just wonder how “ESP” from App Password is linked in the code? Any hint will be appreciated

    Reply
  41. I am trying to also use Firebase library mobizt/Firebase Arduino Client Library for ESP8266 and ESP32@^4.4.14 but I get all kinds of errors. If I run standalone firebase or mobizt/ESP Mail Client@^3.4.19 run fine. It’s only when I merge them. Do you have any hints that may help me?
    Thanks in advance

    Reply
  42. Dear Sara, dear Rui,
    again, your code worked 1:1 out of the box! Thank you very much for this!
    Now, I would like to extend the code to trigger a photo two times a day.
    However, the internal clock is just updated inside the function SmtpCallback only once.
    How can I manage this, to update the clock regularly and extract the time?
    Thank you in advance!
    Regards,
    Juergen

    Reply
    • Dear Sara, dear Rui,
      I managed it all by myself. Sorry for asking, without having tried everything.
      First thing was to get the actual time within the main loop.
      Then extract the actual hour as string, convert it into an integer using the function atoi (hour = atoi(hourstr);) and finally program a scheduler to trigger a photo and send it:
      if ((hour == 6) && (done == false)) {
      capturePhotoSaveLittleFS();
      sendPhoto();
      done = true;}
      The boolean done blocks multiple photos.
      Again, thank you very much for the initial code!
      Best regards, Juergen

      Reply
  43. Dear Sara, dear Rui,
    the code works ok, it takes two times a day a photo, BUT: the time synced with NTP does not work as expected:
    I set the NTP time to UTC + 1h (Germany) plus 1h daylight correction:
    config.time.ntp_server = F(“de.pool.ntp.org,pool.ntp.org,time.nist.gov”); //
    config.time.gmt_offset = 1; // für uns nur eine Stunde
    config.time.day_light_offset = 1;
    HOWEVER, when programmed to take a photo at 16:00, it always makes the photo at 16:59.
    Do you have an idea why this happens?

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.