Cách gửi Email bằng SMTP Server bằng ESP32: HTML hoặc văn bản

Mình sẽ giới thiệu đến bạn cách dùng ESP32 gửi email, thông qua SMTP Server. Chúng ta sẽ gửi email ở cả 2 định dạng khác nhau: gửi HTML hoặc đoạn văn bản thông thường. Mình sẽ sử dụng Arduino IDE, cùng theo dõi nhé!

Giới thiệu và cách cài đặt SMTP Server

Để gửi email thông qua ESP32, chúng ta cần dùng một thư viện có tên là ESP-Mail-Client library. Thư viện này cho phép chúng ta gửi và nhận Email thông qua IMAP hoặc SMTP Server.

Trong hướng dẫn này, IoTZone sẽ giới thiệu đến bạn cách gửi email không đính kèm lẫn có đính kèm tập tin / hình ảnh. Các email đã gửi đi sẽ được lưu trong Filesystem của ESP32 (LittleFS, SPIFFS hoặc FatFs).

Để cài đặt thư viện này, bạn click vào Sketch >> Include Library >> Manage Librarie trong phần mềm Arduino IDE, sau đó tìm ESP Mail Client rồi cài đặt là được nhé:

Giới thiệu và cách cài đặt SMTP Server

Chuẩn bị trước khi dùng ESP32 gửi Email

Tạo tài khoản Gmail mới

Trước khi đi vào hướng dẫn dự án, mình khuyến khích bạn nên tạo một Email mới để dùng nó làm địa chỉ gửi Email. Bạn không nên dùng Email của bạn để làm email gửi đi, vì nếu code bị sai hoặc gặp vấn đề nào đó, thì tài khoản Email của bạn có thể bị vô hiệu hóa hoặc bị xóa đi.

Cách đơn giản nhất là bạn hãy tạo một Email trên Gmail nhé!

ESP32 gửi Email qua tài khoản mới

Sau đó, bạn cần tạo một mật khẩu ứng dụng (App passwords) để ESP32 có thể gửi Email bằng tài khoản này. Mật khẩu ứng dụng gồm 16 chữ số, cho phép các ứng dụng kém an toàn có thể truy cập vào tài khoản Gmail này.

Mật khẩu ứng dụng chỉ có thể được cài khi bạn đã bật xác minh 2 bước, theo hướng dẫn sau:

  • Mở tài khoản Google
  • Tại bảng điều hướng, click vào Security
  • Dưới dòng chữ “Signing in to Google”, bạn chọn vào 2-step verification và chọn Get started
  • Làm theo các hướng dẫn trên màn hình

Sau khi bật xác minh 2 bước, bạn làm theo hướng dẫn sau để cài app passwords:

  • Mở tài khoản Google
  • Tại bảng điều hướng, click vào Security
  • Dưới dòng chữ “Signing in to Google”, bạn chọn vào App passwords.
  • Trong vùng chọn app, bạn chọn vào mail
  • Trong vùng chọn thiết bị (device), bạn chọn vào Other và điền tên vào, ví dụ như ESP32
  • Click vào Generate. Một popup mới hiển thị, có mật khẩu trong đó. Đây sẽ là mật khẩu bạn cần dùng để cho phép ESP8266 / ESP32 gửi Email, nên bạn phải ghi lại chúng nhé!
Lấy app passwords để ESP32 gửi Email

Nếu trong trường hợp bạn tạo Email ở một nhà cung cấp nào khác không phải Google, bạn có thể search trên Google với tên nhà cung cấp bạn đã dùng để tìm cách lấy App passwords nhé!

Cài đặt SMTP Gmail Server

Nếu bạn dùng Gmail thì dưới đây là thông tin chi tiết của SMTP Server:

  • Địa chỉ của SMTP Server: smtp.gmail.com
  • Tên người dùng của SMTP: Địa chỉ Email
  • Mật khẩu SMTP: Mật khẩu của địa chỉ Email bạn đã tạo
  • SMTP port (TLS): 587
  • SMTP port (SSL): 465
  • Yêu cầu SMTP TLS/SSL: có

Cài đặt Outlook SMTP Server

Nếu bạn dùng tài khoản của Outlook, thì dưới đây là thông tin cài đặt chi tiết của SMTP Server:

  • Địa chỉ SMTP Server: smtp.office365.com
  • Tên người dùng + mật khẩu SMTP: Địa chỉ và mật khẩu của Email
  • SMTP Port: 587
  • Yêu cầu SMTP TLS/SSL: có

Cài đặt Live hoặc Hotmail SMTP Server

Nếu bạn dùng tài khoản của Hotmail hoặc Live, thì đây là thông tin:

  • Địa chỉ SMTP Server: smtp.live.com
  • Tên người dùng + mật khẩu SMTP: Địa chỉ và mật khẩu của Email
  • Cổng SMTP: 587
  • Yêu cầu SMTP TLS/SSL: có

Cách gửi Email không đính kèm tập tin với ESP32

Dưới đây là đoạn code cho phép chúng ta gửi Email chỉ gồm HTML hoặc các đoạn văn bản thông qua máy chủ SMTP. Mình đã viết code để ESP32 tự gửi 1 email khi khởi động, bạn có thể chỉnh sửa lại sao cho phù hợp với dự án và mục đích của bạn nhé!

Lưu ý: Khi nạp code vào ESP32 xong, bạn cần thay đổi thông tin mạng WiFi, email người gửi, thông tin về SMTP Server, người nhận và tin nhắn cần gửi thì code mới hoạt động nhé!

#include <Arduino.h>
#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
#include <ESP_Mail_Client.h>

#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com */
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465

/* The sign in credentials */
#define AUTHOR_EMAIL "YOUR_EMAIL@XXXX.com"
#define AUTHOR_PASSWORD "YOUR_EMAIL_APP_PASS"

/* Recipient's email*/
#define RECIPIENT_EMAIL "RECIPIENTE_EMAIL@XXXX.com"

/* Declare the global used SMTPSession object for SMTP transport */
SMTPSession smtp;

/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);

void setup(){
  Serial.begin(115200);
  Serial.println();
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(300);
  }
  Serial.println();
  Serial.print("Connected with IP: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  /*  Set the network reconnection option */
  MailClient.networkReconnect(true);

  /** Enable the debug via Serial port
   * 0 for no debugging
   * 1 for basic level debugging
   *
   * Debug port can be changed via ESP_MAIL_DEFAULT_DEBUG_PORT in ESP_Mail_FS.h
   */
  smtp.debug(1);

  /* Set the callback function to get the sending results */
  smtp.callback(smtpCallback);

  /* Declare the Session_Config for user defined session credentials */
  Session_Config config;

  /* Set the session config */
  config.server.host_name = SMTP_HOST;
  config.server.port = SMTP_PORT;
  config.login.email = AUTHOR_EMAIL;
  config.login.password = AUTHOR_PASSWORD;
  config.login.user_domain = "";

  /*
  Set the NTP config time
  For times east of the Prime Meridian use 0-12
  For times west of the Prime Meridian add 12 to the offset.
  Ex. American/Denver GMT would be -6. 6 + 12 = 18
  See https://en.wikipedia.org/wiki/Time_zone for a list of the GMT/UTC timezone offsets
  */
  config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
  config.time.gmt_offset = 7;
  config.time.day_light_offset = 0;

  /* Declare the message class */
  SMTP_Message message;

  /* Set the message headers */
  message.sender.name = F("ESP");
  message.sender.email = AUTHOR_EMAIL;
  message.subject = F("ESP Test Email");
  message.addRecipient(F("Sara"), RECIPIENT_EMAIL);
    
  /*Send HTML message*/
  /*String htmlMsg = "<div style=\"color:#2f4468;\"><h1>Hello World!</h1><p>- Sent from ESP board</p></div>";
  message.html.content = htmlMsg.c_str();
  message.html.content = htmlMsg.c_str();
  message.text.charSet = "us-ascii";
  message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit;*/

   
  //Send raw text message
  String textMsg = "Hello World! - Sent from ESP board";
  message.text.content = textMsg.c_str();
  message.text.charSet = "us-ascii";
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
  
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
  message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;


  /* Connect to the server */
  if (!smtp.connect(&config)){
    ESP_MAIL_PRINTF("Connection error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
    return;
  }

  if (!smtp.isLoggedIn()){
    Serial.println("\nNot yet logged in.");
  }
  else{
    if (smtp.isAuthenticated())
      Serial.println("\nSuccessfully logged in.");
    else
      Serial.println("\nConnected with no Auth.");
  }

  /* Start sending Email and close the session */
  if (!MailClient.sendMail(&smtp, &message))
    ESP_MAIL_PRINTF("Error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());

}

void loop(){
}

/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status){
  /* Print the current status */
  Serial.println(status.info());

  /* Print the sending result */
  if (status.success()){
    // ESP_MAIL_PRINTF used in the examples is for format printing via debug Serial port
    // that works for all supported Arduino platform SDKs e.g. AVR, SAMD, ESP32 and ESP8266.
    // In ESP8266 and ESP32, you can use Serial.printf directly.

    Serial.println("----------------");
    ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Message sent failed: %d\n", status.failedCount());
    Serial.println("----------------\n");

    for (size_t i = 0; i < smtp.sendingResult.size(); i++)
    {
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);

      // In case, ESP32, ESP8266 and SAMD device, the timestamp get from result.timestamp should be valid if
      // your device time was synched with NTP server.
      // Other devices may show invalid timestamp as the device time was not set i.e. it will show Jan 1, 1970.
      // You can call smtp.setSystemTime(xxx) to set device time manually. Where xxx is timestamp (seconds since Jan 1, 1970)
      
      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %s\n", MailClient.Time.getDateTimeString(result.timestamp, "%B %d, %Y %H:%M:%S").c_str());
      ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
      ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
    }
    Serial.println("----------------\n");

    // You need to clear sending result as the memory usage will grow up.
    smtp.sendingResult.clear();
  }
}

Giải thích chương trình

Mình đã tùy chỉnh code này từ một code mẫu mà thư viện có sẵn, dưới đây mình sẽ giải đáp chi tiết:

Chèn thông tin mạng WIFI:

#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

Chèn cài đặt HTMP Server:

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

Thông tin Email gửi:

#define AUTHOR_EMAIL "YOUR_EMAIL@XXXX.com"
#define AUTHOR_PASSWORD "YOUR_EMAIL_PASS"

Email người nhận:

#define RECIPIENT_EMAIL "RECIPIENTE_EMAIL@XXXX.com"

Tùy chỉnh gtm_offset để email gửi đúng thời gian mình muốn, như ở Việt Nam thì chúng ta dùng số 7:

config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
config.time.gmt_offset = 7;
config.time.day_light_offset = 0;

Đặt tiêu đề Email:

  /* Set the message headers */
  message.sender.name = F("ESP");
  message.sender.email = AUTHOR_EMAIL;
  message.subject = F("ESP Test Email");
  message.addRecipient(F("Sara"), RECIPIENT_EMAIL);

Nội dung Email gồm các đoạn văn bản:

//Send raw text message
String textMsg = "Hello World - Sent from ESP board";
message.text.content = textMsg.c_str();
message.text.charSet = "us-ascii";
message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

Nếu bạn muốn gửi code HTML, bạn có thể xóa các dòng sau (và bạn nên chèn HTML vào biến htmlMsg):

/*Send HTML message*/
/*String htmlMsg = "<div style=\"color:#2f4468;\"><h1>Hello World!</h1><p>- Sent from ESP board</p></div>";
message.html.content = htmlMsg.c_str();
message.html.content = htmlMsg.c_str();
message.text.charSet = "us-ascii";
message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit;*/

Gửi tin nhắn đi:

if (!MailClient.sendMail(&smtp, &message))
  Serial.println("Error sending Email, " + smtp.errorReason());

Kết quả

Bạn hãy upload code lên ESP32, bật Serial Monitor ở tốc độ 115200 và nhấn reset mạch ESP32.

Trên màn hình Serial sẽ hiển thị dòng tin nhắn sau:

Gửi email qua SMTP Server bằng ESP32 thành công
Gửi email qua SMTP Server bằng ESP32 thành công

Bạn có thể mở email người nhận của mình và kiểm tra xem thử nhé (nhờ lưu ý kểm tra cả hộp thư spam lẫn quảng cáo nếu không tìm thấy):

Gửi email qua SMTP Server bằng ESP32 thành công

Nếu bạn gửi đoạn code HTML qua Email, thì Email bạn nhận được sẽ như hình:

Gửi email qua SMTP Server bằng ESP32 thành công

Lời kết

Trên đây là cách gửi Email qua SMTP Server, sử dụng ESP32 đơn giản nhất. Bài viết khá dài rồi nên mình sẽ bổ sung phần gửi tệp đính kèm qua Email vào bài viết sau, bạn theo dõi đón đọc nhé!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *