ESP32からメールを送る

概要

ESP32にはHTTPSクライアントライブラリがあるので、SMTPプロトコルを使ってGmailを送信してみました。意外にもうまくいってしまったので、共有します。

Arduino上で動くコードです。esp32-arduino-coreは最新のものを使用してください。古いものだと、SSLまわりでエラーが発生します。

ソースコード

ここからダウンロードできます。

Mailer.h

本当はあまりよくないのですが、ヘッダーファイルにコードを全部書いてしまったので、Mailer.cppファイルはありません。

SMTPプロトコルに従ってリクエストを送っています。

/*
    ESP32 E-mail Library
    Author:  kerikun11 (GitHub: kerikun11)
    Date:    2017.04.08
*/
#pragma once

#include <WiFiClientSecure.h>
#include <base64.h>

class Mailer {
  public:
    Mailer(const char* username, const char* password, const char* from_address, const int smtp_port = 465, const char* smtp_hostname = "smtp.gmail.com"):
      username(username), password(password), from_address(from_address), smtp_port(smtp_port), smtp_hostname(smtp_hostname) {}

    bool send(const String& to_address, const String& subject, const String& content) {
      WiFiClientSecure client;
      log_d("Connecting to %s\n", smtp_hostname);
      if (!client.connect(smtp_hostname, smtp_port)) {
        log_e("Could not connect to mail server");
        return false;
      }
      if (!readResponse(client, "220")) {
        log_e("Connection Error");
        return false;
      }
      client.println("HELO friend");
      if (!readResponse(client, "250")) {
        log_e("identification error");
        return false;
      }
      client.println("AUTH LOGIN");
      if (!readResponse(client, "334")) {
        log_e("AUTH LOGIN failed");
        return false;
      }
      client.println(base64::encode(username));
      if (!readResponse(client, "334")) {
        log_e("AUTH LOGIN failed");
        return false;
      }
      client.println(base64::encode(password));
      if (!readResponse(client, "235")) {
        log_e("SMTP AUTH error");
        return false;
      }
      client.println("MAIL FROM: <" + String(from_address) + '>');
      if (!readResponse(client, "250")) {
        log_e("MAIL FROM failed");
        return false;
      }
      client.println("RCPT TO: <" + to_address + '>');
      if (!readResponse(client, "250")) {
        log_e("RCPT TO failed");
        return false;
      }
      client.println("DATA");
      if (!readResponse(client, "354")) {
        log_e("SMTP DATA error");
        return false;
      }
      client.println("From: <" + String(from_address) + ">");
      delay(100);
      client.println("To: <" + to_address + ">");
      delay(100);
      client.println("Subject: " + subject);
      delay(100);
      client.println("Mime-Version: 1.0");
      delay(100);
      client.println("Content-Type: text/html");
      delay(100);
      client.println();
      delay(100);
      client.println(content);
      delay(100);
      client.println(".");
      if (!readResponse(client, "250")) {
        log_e("Sending message error");
        return false;
      }
      client.println("QUIT");
      if (!readResponse(client, "221")) {
        log_e("QUIT failed");
        return false;
      }
      log_i("Sending E-mail Successful");
      return true;
    }

  private:
    const char* username;
    const char* password;
    const char* from_address;
    const int smtp_port;
    const char* smtp_hostname;

    bool readResponse(WiFiClientSecure &client, const String &target, uint32_t timeout_ms = 3000) {
      uint32_t timeStamp = millis();
      while (1) {
        if (client.available()) break;
        if (millis() > timeStamp + timeout_ms) {
          log_e("SMTP Response TIMEOUT!");
          return false;
        }
        delay(1);
      }
      String res = client.readStringUntil('\n');
      res.trim();
      log_d("Response: %s", res.c_str());
      if (target != "" && res.indexOf(target) == -1) return false;
      return true;
    }
};

mail.ino

最初に定義してある定数は適当に埋めてください。

/*
    ESP32 E-mail sample
    Author:  kerikun11 (GitHub: kerikun11)
    Date:    2017.04.08
*/

#include <WiFi.h>
#include "esp32-hal-log.h"
#include "Mailer.h"

const char* wifi_ssid = "wifi_ssid";
const char* wifi_pass = "wifi_pass";
const char* smtp_username = "username";
const char* smtp_password = "password";
const char* smtp_from_address = "username@gmail.com";
const int smtp_port = 465;
const char* smtp_hostname = "smtp.gmail.com";

const char* to_address = "hoge@sample.com";
const char* subject = "From ESP32";
const char* content = "Hello, this is ESP32.";

Mailer mail(smtp_username, smtp_password, smtp_from_address, smtp_port, smtp_hostname);

void setup() {
  Serial.begin(115200);
  log_i("Hello, this is ESP32.");

  log_i("Connecting to %s", wifi_ssid);
  WiFi.begin(wifi_ssid, wifi_pass);
  while (WiFi.status() != WL_CONNECTED) {
    log_i("wait...");
    delay(1000);
  }
  log_i("WiFi connected");
  log_i("IP Address: %s", WiFi.localIP().toString().c_str());

  mail.send(to_address, subject, content);
}

void loop() {
}

Gmailで送信するときの注意

Gmailは他のメールよりセキュリティが強いのかわかりませんが、Googleのアカウント設定からセキュリティの低いアプリを許可する必要があります。

Google設定画面

Google設定画面

一番下の方にある安全性の低いアプリを許可する。

Google設定画面

Google設定画面