---
# System prepended metadata

title: 遙控拍攝及電郵傳送影像(使用Blynk)
tags: [iot project, ESP32, CAM]

---

# 遙控拍攝及電郵傳送影像(使用Blynk)
###### tags: `iot project` `ESP32` `CAM`
## 目的﹕用Blynk app控制ESP32攝影機並即時電郵照片。

## 流程﹕
![](https://i.imgur.com/s2bdR8r.png)
1.按拍攝掣
![](https://i.imgur.com/viWwX1m.jpg)
2.隨即收到電郵
![](https://i.imgur.com/v7xYa9L.jpg)
![](https://i.imgur.com/DtY4Wzs.jpg)

## 材料﹕便用一體化的ESP32+攝影機模組
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.23062e8dKwuvn0&id=636886868914&_u=gd5ed5tc877

## Blynk
新增項目
![](https://i.imgur.com/sLRr9St.jpg)
按Create產生Token(待會使用)
![](https://i.imgur.com/iCpaU0y.jpg)
新增Styled Button, 設定如下﹕
OUTPUT是V1
![](https://i.imgur.com/qAxlsbx.jpg)
新增Email, 設定如下(不用填寫電郵(在程式設定))﹕
![](https://i.imgur.com/AMKNa4r.jpg)

## Arduino
選擇ESP32 Wrover Module
![](https://i.imgur.com/dd3RKwL.png)
選擇Huge App (3MB No OTA/1MB SPIFFS)
![](https://i.imgur.com/VgsK59K.png)

程式﹕
```c=
/*********
  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.
*********/
#define BLYNK_PRINT Serial
#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 <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include "time.h"

// REPLACE WITH YOUR NETWORK CREDENTIALS
char auth[] = "";
// You can also specify server:
//Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 80);
//Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
//**Change ↓
char server[] = "";
char ssid[] = "";
char pass[] = "";
//**Change ↑
char showtime[50];

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 28800; //HK time
const int   daylightOffset_sec = 3600;

// 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    ""
#define emailSenderPassword   ""
#define smtpServer            ""
#define smtpServerPort        465
#define emailSubject          "遠程拍攝傳送"
#define emailRecipient        ""


#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"

BLYNK_WRITE(V1)
{
  Serial.println("event triggers");
  Serial.println("should send email...");
  //emailOnButtonPress();
  capturePhotoSaveSpiffs();
  sendPhoto();
}

void printLocalTime(){
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  
  //"strftime"-Format time as string
  //http://www.cplusplus.com/reference/ctime/strftime/
  strftime(showtime,50, "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.print("As taken photo time:");
  Serial.println(showtime);

  /* Serial.print("Day of week: ");
  Serial.println(&timeinfo, "%A");
  Serial.print("Month: ");
  Serial.println(&timeinfo, "%B");
  Serial.print("Day of Month: ");
  Serial.println(&timeinfo, "%d");
  Serial.print("Year: ");
  Serial.println(&timeinfo, "%Y");
  Serial.print("Hour: ");
  Serial.println(&timeinfo, "%H");
  Serial.print("Hour (12 hour format): ");
  Serial.println(&timeinfo, "%I");
  Serial.print("Minute: ");
  Serial.println(&timeinfo, "%M");
  Serial.print("Second: ");
  Serial.println(&timeinfo, "%S");

  Serial.println("Time variables");
  char timeHour[3];
  strftime(timeHour,3, "%H", &timeinfo);
  Serial.println(timeHour);
  char timeWeekDay[10];
  strftime(timeWeekDay,10, "%A", &timeinfo);
  Serial.println(timeWeekDay); */
  Serial.println();

}

void setup() {
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, server, 8080);
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  Serial.println();

  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  //printLocalTime();
  // Connect to Wi-Fi
  //WiFi.begin(ssid, password);
  //Serial.print("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");
  }
  
  // Print ESP32 Local IP Address
  //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;
  }
  
  
}

void loop() {
  Blynk.run();
}

// 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();

    //Call printLocalTime as the photo taken time and record/update it
    printLocalTime();

    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("遠程拍攝傳送", 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
  //printLocalTime();
  String emailMsg = "拍攝時間(香港標準時間)：";
  emailMsg += showtime;
  smtpData.setMessage(emailMsg, 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());
    Blynk.notify("傳送電郵出錯:" + MailClient.smtpErrorReason());
  } else{
    Blynk.notify("已電郵最新照片");
  }
  // 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());
}

```

### 程式資料設定
![](https://i.imgur.com/CZzbhjR.png)
```c
char auth[] = ""; //新增Blynk應用時的Token
```
```c
char server[] = ""; //預設 blynk-cloud.com
char ssid[] = ""; //ESP32 要盈入的Wifi ssid
char pass[] = ""; //Wifi 密碼
```
```c
//僅以gmail為例
#define emailSenderAccount    "" //sender@gmail.com
#define emailSenderPassword   "" //emailPassword
#define smtpServer            "" //"smtp.gmail.com"
#define smtpServerPort        465
#define emailSubject          "遠程拍攝傳送"
#define emailRecipient        "" //whoreceive@gmail.com
```
### 上載程式注意﹕
見
```
Connecting........_____....._____....._____....
```
時要FLUSH(Press)->RST(Press)->FLUSH(Release)->RST(Release)
(動畫)
![](https://i.imgur.com/i4iYHm0.gif)
才能上載程式

https://github.com/snakeleader/blynk_sent_email








