SSLメールでメール送信してみた その4
Gmailでもできそうだよという話。
初回で断念してましたができそうです。
m5stack-build.hatenablog.com
参考サイト
アプリパスワードをメールログインのパスワードに使用すればできそうです。
ここのサイトにアプリパスワードの作成方法が記載されているので参考にしてみてください。
後はgmailで必要なルート証明書を使えばGmailでもメール送信できそうです。(私は確認してないです。。。)
www.howtonote.jp
SSLメールでメール送信してみた その3
前回に引き続きyahooメールのSSLメールをやってみました。
今回は設定系はiniファイルに持っていくことにしました。
変更しても再度コンパイルする必要がなくなりますからね。
m5stack-build.hatenablog.com
サンプルコード(mail.ino)
#include <M5Core2.h> #include <WiFi.h> #include "Mailer.h" const char* subject = "From M5stack"; const char* content = "Hello, this is M5stack Core2."; void setup() { String config_ini; String ssid; String password; String mail_username; String mail_pass; String mail_from_address; String mail_to_address; String smtp_server; String smtp_port; unsigned short ausIdx = 0; /* 初期化 */ M5.begin(); /* SD初期化 */ SD.begin(); Serial.begin(115200); delay(10); /* 文字サイズを変更 */ M5.Lcd.setTextSize(1); /* ファイルオープン */ File datFile = SD.open("/set/config.ini"); if( datFile ) { M5.Lcd.println("File open successful"); Serial.println("File open successful"); /* サイズ分ループ */ while( datFile.available() ) { config_ini = config_ini + datFile.readString(); } /* ファイルクローズ */ datFile.close(); } else { M5.Lcd.println("File open error hello.txt"); } /* SSID取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID\r\n") + 7); ssid = config_ini.substring(0, config_ini.indexOf("\r\n")); /* パスワード取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID_PASS\r\n") + 12); password = config_ini.substring(0, config_ini.indexOf("\r\n")); /* connect to WiFi */ M5.Lcd.print("Connecting to YOUR_SSID "); Serial.println("Connecting to YOUR_SSID "); /* wifi初期化 */ WiFi.begin(ssid.c_str(), password.c_str()); while (WiFi.status() != WL_CONNECTED) { delay(500); M5.Lcd.print("."); } M5.Lcd.println(" CONNECTED"); Serial.println(" CONNECTED"); /* メールユーザ名取得 */ config_ini = config_ini.substring(config_ini.indexOf("#MAIL_USERNAME\r\n") + 16); mail_username = config_ini.substring(0, config_ini.indexOf("\r\n")); /* メールパスワード取得 */ config_ini = config_ini.substring(config_ini.indexOf("#MAIL_PASS\r\n") + 12); mail_pass = config_ini.substring(0, config_ini.indexOf("\r\n")); /* 送信元アドレス取得 */ config_ini = config_ini.substring(config_ini.indexOf("#MAIL_FROM_ADDR\r\n") + 17); mail_from_address = config_ini.substring(0, config_ini.indexOf("\r\n")); /* 送信先アドレス取得 */ config_ini = config_ini.substring(config_ini.indexOf("#MAIL_TO_ADDR\r\n") + 15); mail_to_address = config_ini.substring(0, config_ini.indexOf("\r\n")); /* SMTPポート番号取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SMTP_PORT\r\n") + 12); smtp_port = config_ini.substring(0, config_ini.indexOf("\r\n")); /* SMTPサーバアドレス取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SMTP_SERVER\r\n") + 14); smtp_server = config_ini.substring(0, config_ini.indexOf("\r\n")); /* disconnect WiFi as it's no longer needed */ // WiFi.disconnect(true); // WiFi.mode(WIFI_OFF); /* メールクラス コンストラクタ */ Mailer mail(mail_username.c_str(), mail_pass.c_str(), mail_from_address.c_str(), atoi(smtp_port.c_str()), smtp_server.c_str()); /* メール送信 */ mail.send(mail_to_address.c_str(), subject, content); } void loop() { delay(10000); }
iniファイルに格納して読みだす方法は昔の記事ですが参考までに。。
m5stack-build.hatenablog.com
サンプルコード(Mailer.h)
2022/06/19 追記
証明書もファイルから読み出すように変更した。
/* ESP32 E-mail Library Author: kerikun11 (Github: kerikun11) Date: 2017.04.08 */ #pragma once #include <ssl_client.h> #include <WiFiClientSecure.h> #include <base64.h> class Mailer { public: Mailer(const char* username, const char* password, const char* from_address, const int smtp_port, const char* smtp_hostname): 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; String root_ca; /* ファイルオープン */ File datFile = SD.open("/set/yahoo_root.cer"); /* 証明書を読みだす */ if( datFile ) { M5.Lcd.println("Root File open successful"); Serial.println("Root File open successful"); /* サイズ分ループ */ while( datFile.available() ) { root_ca = root_ca + datFile.readString(); } /* ファイルクローズ */ datFile.close(); } else { M5.Lcd.println("Root File open error"); } client.setCACert(root_ca.c_str()); /* 証明書の設定 */ M5.Lcd.printf("Connecting to %s\n", smtp_hostname); Serial.printf("Connecting to %s\n", smtp_hostname); if (!client.connect(smtp_hostname, smtp_port)) { Serial.println("Could not connect to mail server"); return false; } if (!readResponse(client, "220")) { Serial.println("Connection Error"); return false; } client.println("HELO friend"); if (!readResponse(client, "250")) { Serial.println("identification error"); return false; } client.println("AUTH LOGIN"); if (!readResponse(client, "334")) { Serial.println("AUTH LOGIN failed"); return false; } client.println(base64::encode(username)); if (!readResponse(client, "334")) { Serial.println("AUTH LOGIN failed"); return false; } client.println(base64::encode(password)); if (!readResponse(client, "235")) { Serial.println("SMTP AUTH error"); return false; } client.println("MAIL FROM: <" + String(from_address) + '>'); if (!readResponse(client, "250")) { Serial.println("MAIL FROM failed"); return false; } client.println("RCPT TO: <" + to_address + '>'); if (!readResponse(client, "250")) { Serial.println("RCPT TO failed"); return false; } client.println("DATA"); if (!readResponse(client, "354")) { Serial.println("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")) { Serial.println("Sending message error"); return false; } client.println("QUIT"); if (!readResponse(client, "221")) { Serial.println("QUIT failed"); return false; } M5.Lcd.println("Sending E-mail Successful"); Serial.println("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 = 10000) { uint32_t timeStamp = millis(); while (1) { if (client.available()) break; if (millis() > timeStamp + timeout_ms) { Serial.println("SMTP Response TIMEOUT!"); return false; } delay(1); } String res = client.readStringUntil('\n'); res.trim(); Serial.printf("Response: %s\n", res.c_str()); if (target != "" && res.indexOf(target) == -1) return false; return true; } };
SSLメールでメール送信してみた その2
前回に引き続きyahooメールのSSLメールをやってみました。
m5stack-build.hatenablog.com
参考サイト
参考にさせていただきました。ありがとうございます。
www.kerislab.jp
参考にさせていただいソースからの変更点
・SMTPサーバのアドレス。詳しくはこちら→Yahoo!メールヘルプ
・証明書の設定。SSLには証明書が必要です。
・SSLメールは証明書の検証とかで通常のメールより少し時間がかかるのでタイムアウト値を長くしています。
サンプルコード(mail.ino)
#include <M5Core2.h> #include <WiFi.h> #include "Mailer.h" const char* smtp_username = "username"; const char* smtp_password = "password"; const char* smtp_from_address = "user@yahoo.co.jp"; const int smtp_port = 465; const char* smtp_hostname = "smtp.mail.yahoo.co.jp"; const char* to_address = "user@gmail.com"; const char* subject = "From M5stack"; const char* content = "Hello, this is M5stack Core2."; Mailer mail(smtp_username, smtp_password, smtp_from_address, smtp_port, smtp_hostname); void setup() { String config_ini; String ssid; String password; unsigned short ausIdx = 0; /* 初期化 */ M5.begin(); /* SD初期化 */ SD.begin(); Serial.begin(115200); delay(10); /* 文字サイズを変更 */ M5.Lcd.setTextSize(1); /* ファイルオープン */ File datFile = SD.open("/set/config.ini"); if( datFile ) { M5.Lcd.println("File open successful"); Serial.println("File open successful"); /* サイズ分ループ */ while( datFile.available() ) { config_ini = config_ini + datFile.readString(); } /* ファイルクローズ */ datFile.close(); } else { M5.Lcd.println("File open error hello.txt"); } /* SSID取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID\r\n") + 7); ssid = config_ini.substring(0, config_ini.indexOf("\r\n")); /* パスワード取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID_PASS\r\n") + 12); password = config_ini.substring(0, config_ini.indexOf("\r\n")); /* connect to WiFi */ M5.Lcd.print("Connecting to YOUR_SSID "); Serial.println("Connecting to YOUR_SSID "); /* wifi初期化 */ WiFi.begin(ssid.c_str(), password.c_str()); while (WiFi.status() != WL_CONNECTED) { delay(500); M5.Lcd.print("."); } M5.Lcd.println(" CONNECTED"); Serial.println(" CONNECTED"); /* disconnect WiFi as it's no longer needed */ // WiFi.disconnect(true); // WiFi.mode(WIFI_OFF); mail.send(to_address, subject, content); } void loop() { delay(10000); }
サンプルコード(Mailer.h)
/* ESP32 E-mail Library Author: kerikun11 (Github: kerikun11) Date: 2017.04.08 */ #pragma once #include <ssl_client.h> #include <WiFiClientSecure.h> #include <base64.h> const char* yahoo_root_ca= \ "-----BEGIN CERTIFICATE-----\n" \ ここに証明書の内容 "-----END CERTIFICATE----- \n"; class Mailer { public: Mailer(const char* username, const char* password, const char* from_address, const int smtp_port = 465, const char* smtp_hostname = "smtp.mail.yahoo.co.jp"): 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; client.setCACert(yahoo_root_ca); /* ここで証明書の設定 */ Serial.printf("Connecting to %s\n", smtp_hostname); if (!client.connect(smtp_hostname, smtp_port)) { Serial.println("Could not connect to mail server"); return false; } if (!readResponse(client, "220")) { Serial.println("Connection Error"); return false; } client.println("HELO friend"); if (!readResponse(client, "250")) { Serial.println("identification error"); return false; } client.println("AUTH LOGIN"); if (!readResponse(client, "334")) { Serial.println("AUTH LOGIN failed"); return false; } client.println(base64::encode(username)); if (!readResponse(client, "334")) { Serial.println("AUTH LOGIN failed"); return false; } client.println(base64::encode(password)); if (!readResponse(client, "235")) { Serial.println("SMTP AUTH error"); return false; } client.println("MAIL FROM: <" + String(from_address) + '>'); if (!readResponse(client, "250")) { Serial.println("MAIL FROM failed"); return false; } client.println("RCPT TO: <" + to_address + '>'); if (!readResponse(client, "250")) { Serial.println("RCPT TO failed"); return false; } client.println("DATA"); if (!readResponse(client, "354")) { Serial.println("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")) { Serial.println("Sending message error"); return false; } client.println("QUIT"); if (!readResponse(client, "221")) { Serial.println("QUIT failed"); return false; } Serial.println("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 = 10000) { uint32_t timeStamp = millis(); while (1) { if (client.available()) break; if (millis() > timeStamp + timeout_ms) { Serial.println("SMTP Response TIMEOUT!"); return false; } delay(1); } String res = client.readStringUntil('\n'); res.trim(); Serial.printf("Response: %s\n", res.c_str()); if (target != "" && res.indexOf(target) == -1) return false; return true; } };
yahooメールの設定
yahooメール側での設定も必要です。
「Yahoo! JAPAN公式サービス以外からのアクセスも有効にする」を選択し、「IMAP」「POP」「SMTP」を全て「有効にする」を選択します。
SMTPだけ有効にすると「Yahoo! JAPAN公式サービスを利用したアクセスのみ有効にする」が適用されてしまうみたいです。
実行結果
シリアルではサーバとのやり取りを出力しています。
実際に受信したメールがこちら
SSLメールでメール送信してみた その1
まずはSSL通信やってみることに その2
昨日試したSSL通信のサンプルコードです。
ほぼほぼコピペですがsetup()の中身は自分用に少し変えています。
サンプルコード
#include <M5Core2.h> #include <WiFi.h> #include <HTTPClient.h> #define USE_SERIAL Serial const char* root_ca= \ "-----BEGIN CERTIFICATE-----\n" "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" "rqXRfboQnoZsG4q5WTP468SQvvG5\n" "-----END CERTIFICATE-----"; // (2) void setup() { String config_ini; String ssid; String password; unsigned short ausIdx = 0; /* 初期化 */ M5.begin(); /* SD初期化 */ SD.begin(); Serial.begin(115200); delay(10); /* 文字サイズを変更 */ M5.Lcd.setTextSize(1); /* ファイルオープン */ File datFile = SD.open("/set/config.ini"); if( datFile ) { M5.Lcd.println("File open successful"); Serial.println("File open successful"); /* サイズ分ループ */ while( datFile.available() ) { config_ini = config_ini + datFile.readString(); } /* ファイルクローズ */ datFile.close(); } else { M5.Lcd.println("File open error hello.txt"); } /* SSID取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID\r\n") + 7); ssid = config_ini.substring(0, config_ini.indexOf("\r\n")); /* パスワード取得 */ config_ini = config_ini.substring(config_ini.indexOf("#SSID_PASS\r\n") + 12); password = config_ini.substring(0, config_ini.indexOf("\r\n")); /* connect to WiFi */ M5.Lcd.print("Connecting to YOUR_SSID "); Serial.println("Connecting to YOUR_SSID "); /* wifi初期化 */ WiFi.begin(ssid.c_str(), password.c_str()); while (WiFi.status() != WL_CONNECTED) { delay(500); M5.Lcd.print("."); } M5.Lcd.println(" CONNECTED"); Serial.println(" CONNECTED"); /* disconnect WiFi as it's no longer needed */ // WiFi.disconnect(true); // WiFi.mode(WIFI_OFF); } void loop() { while (WiFi.status() == WL_CONNECTED) { HTTPClient http; USE_SERIAL.print("[HTTP] begin...\n"); http.begin("https://qiita.com/robots.txt", root_ca); // (3) USE_SERIAL.print("[HTTP] GET...\n"); int httpCode = http.GET(); if (httpCode > 0) { USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); USE_SERIAL.println(payload); } } else { USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); } delay(10000); }
私の場合はWiFiのSSIDとPASSはiniファイルに格納して読みだしてます。
昔の記事ですが参考までに。。
m5stack-build.hatenablog.com