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

1.按拍攝掣

2.隨即收到電郵


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

按Create產生Token(待會使用)

新增Styled Button, 設定如下﹕
OUTPUT是V1

新增Email, 設定如下(不用填寫電郵(在程式設定))﹕

## Arduino
選擇ESP32 Wrover Module

選擇Huge App (3MB No OTA/1MB SPIFFS)

程式﹕
```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());
}
```
### 程式資料設定

```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://github.com/snakeleader/blynk_sent_email