メールを送る(03)

今回はカメラで撮った写真をメールで送信してみます。

先ずはメールアカウントを取る

先ずはメールアカウントを取る所から始めます。既に持っているメールアカウントを使うなら新たに取る必要は無いですがその場合でも若干追加操作が必要になると思います。

Googleで、”esp32 mail client”で検索すると幾つかHPがリストされます。例えば、”ESP32 Send Emails using an SMTP Server: HTML, Text, and Attachments (Arduino IDE)”。ここを参照にGoogleのアカウントを取って行きます。

申請時に以下を注意して下さい。

  • 2段階認証にする事。
  • 2段階認証後に”App Passwords”と言う欄が追加される。
  • 指示通り進めて行くと、Password が作製されます。
  • この、Password  は後でメールを送信する時に使いますのでメモって下さい。

このHPの先を読んで行くと、メールの送信方法が説明されているのですが、SDカードに保存されたファイルを添付して送信する方法の説明が有りません。残念。

SDカードに保存されているデータの添付方法

ここ ー> ”motion-triggered-image-camera-diy-10/diy-e10-p2/diy-e10-p2.ino にSDカードに保存されたデータを添付してメールを送るソフトが有りました。必要な部分のみ書き出すと、

mail

#include <ESP32_MailClient.h>

#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465

/* The sign in credentials */
#define AUTHOR_EMAIL "aaaaaaaa@gmail.com"
#define AUTHOR_PASSWORD "xxxxxxxxxxxx"

/* Recipient's email*/
#define RECIPIENT_EMAIL "bbbbbbbbbbbbbbb"

/* The SMTP Session object used for Email sending */
SMTPData smtpData;

void setup() 
{
    ..................
    .................
    
    MailClient.sdBegin(sd_sck,sd_miso,sd_mosi,sd_ss);  //SCK, MISO, MOSI, SS

    ..................
    .................
}

void send_mail()
{
    //send email
    Serial.println("Sending email...");
    
    //Set the Email host, port, account and password
    smtpData.setLogin("smtp.gmail.com", 465, AUTHOR_EMAIL, AUTHOR_PASSWORD);
  
    //Set the sender name and Email
    smtpData.setSender("ESP32-CAM", AUTHOR_EMAIL);
  
    //Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest)
    smtpData.setPriority("Normal");

    //Set the subject
    smtpData.setSubject("Take a picture - ESP32-CAM");
    
    //Set the message - normal text or html format
    smtpData.setMessage("Image attached.", false);

    //Add recipients, can add more than one recipient
    smtpData.addRecipient(RECIPIENT_EMAIL);
  
    //Add attach files from SD card
    smtpData.addAttachFile("/data.jpg");
  
    //Set the storage types to read the attach files (SD is default)
    smtpData.setFileStorageType(MailClientStorageType::SD);
  
    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());

    //Do something when complete
    if (msg.success())
    {
        Serial.println("----------------");
    }
}

  • 1行:#include  <ESP32_MailClient.h>
    • メール関係のヘッダー
  • 3行:#define SMTP_HOST ”smtp.gmail.com”
    • Googleの場合のSMTPサーバーです。
  • 4行:#define SMTP_PORT 465
    • ポート番号を指定します。 今回は465を指定
  • 7行:#define AUTHOR_EMAIL “aaaaaaaa@gmail.com”
    • ここに今回使用するメールアドレス(先程新たにアカウントを取ったもの)を指定します。
  • 8行:#define AUTHOR_PASSWORD “xxxxxxxxxxxx”
    • ここに、”App Passwords”で得たパスワードを入力します。
  • 11行:#define RECIPIENT_EMAIL “bbbbbbbbbbbbbbb”
    • ここは、送り先のアドレスです。
  • 14行:SMTPData smtpData;
    • オブジェクトの宣言
  • 21行:MailClient.sdBegin(sd_sck,sd_miso,sd_mosi,sd_ss); //SCK, MISO, MOSI, SS
    • SDカードに保存したファイルを添付する為にこれを実行します。
  • 27行:void send_mail() メールを送る関数
    • 33行:Email host, port, account and passwordを設定
    • 36行:送り主の名前とメールアドレスの設定
    • 39行:優先順位の設定。ここでは、”Normal”に設定
    • 42行:メールの題名の設定
    • 45行:メール本文の設定。
      • 第二引数
        • true: 第一引数が、HTML 表記
        • false: 第一引数が 平文
    • 48行:送り先のメールアドレス。
    • 51行:これで、SDカードに保存されたファイルを添付出来ます。
      • 添付するファールを引数に指定。
      • 添付するファイルはテキストでもイメージでもOK。
      • 追加で他のファイルを添付したい時はもう一度この関数を実行する。
    • 54行:ストレージタイプを設定する。
      • 今回はSDカードなので、SDと指定。
    • 56行:コールバック関数の指定
      • コールバック関数は送信の経過を表示します。
    • 59行:メールの送信
    • 63行:関数の廃止
  • 67行:コールバック関数。
    • 通信の経過を表示します。 

これを前回のプログラムに追加したのが下記です。

sample_03.ino

#include "esp_camera.h"
#include "Arduino.h"
#include "SD.h"
#include "FS.h"

#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>

#include <ESP32_MailClient.h>

// Pin definition for CAMERA_MODEL_WROVER_KIT
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#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      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

// Pin definition for SD Card
#define sd_sck  13
#define sd_mosi 15
#define sd_ss   12
#define sd_miso 14

WebServer server(80);
const char *SSID = "aaaaaaaaaaaa";
const char *PASSWORD = "bbbbbbbbbbbbbbb";

#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465

/* The sign in credentials */
#define AUTHOR_EMAIL "cccccccccc@gmail.com"
#define AUTHOR_PASSWORD "xxxxxxxxxxxxxxxxx"

/* Recipient's email*/
#define RECIPIENT_EMAIL "yyyyyyyyyy"

/* The SMTP Session object used for Email sending */
SMTPData smtpData;


// Take Picture with Camera
camera_fb_t * fb = NULL;
int c_state = 1;

String index_00 =
                  "<!DOCTYPE html>\n<html>\n<head>\n"
                  "<title>ESP32CAM</title>\n"
                  "<style type='text/css'>\n"
                  "button.bt1 {background:#90ee90;font-size:50px;padding:10px;margin:20px;border-radius:30px;box-shadow:4px 4px #555;}\n"
                  "</style>\n</head>\n"
                  "<body>\n<center>\n"
                  "<div style='width:700px; background-color:#d8a373; border-style:solid; border-radius:30px; border-color:#6b3f31'>\n"
                  "<p style='font-size:70px'><b><i><u>ESP32CAM</u></i></b></p>\n";
                  
void setup() 
{

    Serial.begin(115200);
    delay(1000);

    Serial.println("Connecting to WiFi");
    WiFi.disconnect();
    WiFi.softAPdisconnect(true);

    WiFi.mode(WIFI_STA);
    WiFi.begin(SSID, PASSWORD);
    delay(1000);

    // Try forever
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.println("...Connecting to WiFi");
        delay(1000);
    }
    Serial.println("Connected");
    Serial.println(SSID);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    if (MDNS.begin("esp32cam")) 
    {
        Serial.println("MDNS responder started");
    }

    init_cam();

    SPI.begin(sd_sck, sd_miso, sd_mosi, sd_ss);
    SD.begin(sd_ss);

    MailClient.sdBegin(sd_sck,sd_miso,sd_mosi,sd_ss);  //SCK, MISO, MOSI, SS

    server.begin();
    Serial.println("HTTP server started");
    
    server.on("/", handleRoot);
    server.onNotFound(handleWebRequests);
}

void loop() {

  server.handleClient();

}

void send_mail()
{
    //send email
    Serial.println("Sending email...");
    //Set the Email host, port, account and password
    smtpData.setLogin("smtp.gmail.com", 465, AUTHOR_EMAIL, AUTHOR_PASSWORD);
  
    //Set the sender name and Email
    smtpData.setSender("ESP32-CAM", AUTHOR_EMAIL);
  
    //Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest)
    smtpData.setPriority("Normal");

    //Set the subject
    smtpData.setSubject("Take a picture - ESP32-CAM");
    
    //Set the message - normal text or html format
//    smtpData.setMessage("<div style=\"color:#003366;font-size:20px;\">Image captured and attached.</div>", true);
    smtpData.setMessage("Image attached.", false);

    //Add recipients, can add more than one recipient
    smtpData.addRecipient(RECIPIENT_EMAIL);
  
    //Add attach files from SD card
    smtpData.addAttachFile("/data.jpg");
  
    //Set the storage types to read the attach files (SD is default)
    smtpData.setFileStorageType(MailClientStorageType::SD);
  
    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());

    //Do something when complete
    if (msg.success())
    {
        Serial.println("----------------");
    }
}

void init_cam()
{
    camera_config_t config;
    config.ledc_channel = LEDC_CHANNEL_0;
    config.ledc_timer = LEDC_TIMER_0;
    config.pin_d0 = Y2_GPIO_NUM;
    config.pin_d1 = Y3_GPIO_NUM;
    config.pin_d2 = Y4_GPIO_NUM;
    config.pin_d3 = Y5_GPIO_NUM;
    config.pin_d4 = Y6_GPIO_NUM;
    config.pin_d5 = Y7_GPIO_NUM;
    config.pin_d6 = Y8_GPIO_NUM;
    config.pin_d7 = Y9_GPIO_NUM;
    config.pin_xclk = XCLK_GPIO_NUM;
    config.pin_pclk = PCLK_GPIO_NUM;
    config.pin_vsync = VSYNC_GPIO_NUM;
    config.pin_href = HREF_GPIO_NUM;
    config.pin_sscb_sda = SIOD_GPIO_NUM;
    config.pin_sscb_scl = SIOC_GPIO_NUM;
    config.pin_pwdn = PWDN_GPIO_NUM;
    config.pin_reset = RESET_GPIO_NUM;
    config.xclk_freq_hz = 20000000;
    config.pixel_format = PIXFORMAT_JPEG;
    config.frame_size = FRAMESIZE_VGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 10;
    config.fb_count = 2;

    // Init Camera
    esp_camera_init(&config);
}

void handleRoot() {
  String index_dt,cmd;
  File dataFile;

    cmd=server.argName(0);
    switch(cmd.toInt())
    {
      case 1:   // Take photo
                if(c_state)
                {
                    fb = esp_camera_fb_get();
                    dataFile = SD.open("/data.jpg", FILE_WRITE);
                    dataFile.write(fb->buf, fb->len); // payload (image), payload length
                    dataFile.close();
                    esp_camera_fb_return(fb);
                    Serial.printf("Take a photo.\n");
                }
                c_state = !c_state;
                break;

      case 2:   // Send mail
                send_mail();
                break;
    }

    index_dt = index_00;
    cmd = "<img src='/data.jpg'>\n"
          "<form method='get'>\n<button type='submit' name='1' class='bt1'>Stream</button>\n";
    if(c_state)
    {
        cmd = "<img src='/data.strm'>\n"
              "<form method='get'>\n<button type='submit' name='1' class='bt1' onClick='onBtnStearm()'>Photo</button>\n";
    }
              
    index_dt += cmd;
    index_dt += "<button type='submit' name='2' class='bt1' ";
    if(c_state) index_dt += "disabled ";
    index_dt += ">Mail</button>\n</form>\n</div>\n</center>\n<script>\nfunction onBtnStearm() {\n window.stop();\n}\n</script>\n</body>\n</html>";
                        
    server.send(200, "text/html", index_dt);
}

void handleWebRequests()
{
  String dataType = "text/plain";
  String path;
  File dataFile;
  int a;
  WiFiClient client;
  camera_fb_t * fb;

    a = 1;
    path = server.uri();
    if(path.endsWith(".txt")) dataType = "text/plain";
    else if(path.endsWith(".css")) dataType = "text/css";
    else if(path.endsWith(".js")) dataType = "application/javascript";
    else if(path.endsWith(".png")) dataType = "image/png";
    else if(path.endsWith(".gif")) dataType = "image/gif";
    else if(path.endsWith(".jpg")) dataType = "image/jpg";
    else if(path.endsWith(".strm"))
    {
      client = server.client();
      String response = "HTTP/1.1 200 OK\r\n";
      response += "Content-Type: multipart/x-mixed-replace; boundary=--frame\r\n\r\n";
      server.sendContent(response);

      while (1)
      {
        fb = esp_camera_fb_get();
        if (!client.connected())
        {
          esp_camera_fb_return(fb);
          break;
        }

        response = "--frame\r\n";
        response += "Content-Type: image/jpeg\r\n\r\n";
        server.sendContent(response);

        client.write(fb->buf, fb->len);
        server.sendContent("\r\n");
        esp_camera_fb_return(fb);

        if (!client.connected()) break;
      }
      a=0;
    }

    delay(5);

  if(a)
  {
    dataFile = SD.open(path.c_str(), "r");
    server.streamFile(dataFile, dataType);
    dataFile.close();
    delay(5);
  }
}

コンパイルする前にメール関係のライブラリーを追加します。Arduino IDEで ツール ー> ライブラリーの管理 と進むと下記の画面になります。ここで検索欄に、” esp32 mail”と入れると、”ESP32 Mail Client”が表示されます。これをインストールします。

実行

先ずは、経過を表示するのでシリアルモニタをあげて下さい。その後、コンパイルして実行します。

  • シリアルモニタに、HTTP server started と表示されたらブラウザを上げて下さい。
  • ブラウザのURL欄に、esp32cam.local と入力するとこの様な画面が出ます。
  • 前回はPhotoボタンのみでしたが、今回は写真を送る為にMailボタンを追加しています。
  • 先ずはPhotoボタンを押して写真を撮影して下さい。 
  • Mailボタンが有効になりメースの送信が可能になります。
  • メールボタンを押して下さい。シリアルモニターに送信の経過が表示されます。
Monitor

Sending email...
Connecting to SMTP server...
SMTP server connected, wait for response...
Identification...
Authentication...
Sign in...
Sending Email header...
Sending Email body...
Sending attachments...
/data.jpg
Finalize...
Finished
Email sent successfully
----------------

  • 送信が完了したら、メールが届いたか確認して下さい。

こんな感じでメールが届いていて、それを開くと

題名、本文、添付資料。すべてプログラム通りです。

次は

撮影した写真をメールで送れる様になったので用途の幅が広がりました。現在のHPは、解像度、画質が固定されていますがカメラの設定をこれに追加したい所です。サンプルスケッチのHPをもう一度見て追加して行きたいと思います。