Send Email from ESP8266 NodeMCU via SMTP Server

Send Email from ESP8266 NodeMCU via SMTP Server

This article will teach you how to send email from ESP8266 NodeMCU board via SMTP Server. We'll show you how to send an email with plain text, HTML text, and attachments such as images and files (.txt). The Arduino core will be used to program the ESP8266 NodeMCU board.

In this tutorial, we cover the following topics:

Introducing SMTP Servers

SMTP is an internet standard for email transmission that stands for Simple Mail Transfer Protocol. You need to connect the ESP8266 NodeMCU to an SMTP server to send emails using it.

ESP-Mail-Client Library

We'll use the ESP-Mail-Client library to send email from ESP8266 NodeMCU board. The ESP8266 NodeMCU can send and receive emails with or without attachments over SMTP and IMAP servers using this library. If you like this library and plan to use it in your projects, please consider supporting the developer's efforts by visiting the library's GitHub page.

We'll use SMTP in this article to send an email with and without attachments. We'll submit an image (.png) and a text (.txt) file as examples. The email files may be kept in the ESP8266 filesystem (SPIFFS or LittleFS) or on a microSD card (not covered in this article).

Installing the ESP Mail Client Library

You need to install the ESP-Mail-Client library before proceeding with this tutorial. This library cannot be installed using the Arduino IDE Library Manager. To install the library, follow the next steps:

  1. Click here to download the ESP-Mail-Client library.zip folder.
  2. In your Arduino IDE, go to Sketch > Include Library > Add .zip Library.
  3. Select the zip file you’ve just downloaded.

Then, under File > Examples > ESP-Mail-Client, you'll find various examples to test. The examples are also available on the library's GitHub page.

To include the library in your platformio.ini file if you're using the ESP8266 with VS Code and PlatformIO, copy the following to your platformio.ini file:

lib_deps = mobizt/ESP Mail Client @ ^1.1.7

Sender Email (New Account)

We recommend starting a new email account to send emails to your main personal email address. Do not use your primary personal email address to send emails from ESP8266 NodeMCU. You may be banned or have your account temporarily suspended if anything goes wrong in your code or if you submit too many requests by accident.

To send the emails, we'll use a newly created Gmail.com account, but you may use any other email service. The receiver's email address may be your personal email address without issue.

Create a Sender Email Account

Create a new email account to send emails from the ESP8266 NodeMCU board. If you wish to use Gmail, go to this link to create an account.

GMAIL: create a new account

Create an App Password

You'll need to create an app password so that the ESP32 can send emails using your Gmail account. A less secure app or device may access your Google Account with the use of an app password, which is a 16-digit number. Learn more about sign-in with app passwords here.

An app password can only be used with accounts that have 2-step verification turned on.

  1. Open your Google Account.
  2. In the navigation panel, select “Security“.
  3. Under “Signing in to Google“, select “2-Step Verification > Get Started“.
  4. Follow the on-screen steps.

You may create an app password after enabling 2-step verification.

  1. Open your Google Account.
  2. In the navigation panel, select “Security“.
  3. Under “Signing in to Google”, select “App Passwords“.
create app password gmail
  1. Choose mail in the Select App field. Give the device a name, such as ESP32, and select “Other“. Then, click on “Generate“. When you use ESP32 or ESP8266 to send emails, a window with a password will pop up. You'll need that password later, even though it says you don't need to remember it.
generated app password gmail

You should now have an app password that you'll use to send emails from the ESP32 code.

Gmail app password created for esp8266 send emails

Check out how to create an app password if you're using another email provider. A quick Google search for “your_email_provider + create app password” should provide the instructions.

Gmail SMTP Server Settings

If you’re using a Gmail account, these are the SMTP Server details:

  • SMTP Server: smtp.gmail.com
  • SMTP username: Complete Gmail address
  • SMTP password: Your Gmail password
  • SMTP port (TLS): 587
  • SMTP port (SSL): 465
  • SMTP TLS/SSL required: yes

Outlook SMTP Server Settings

For Outlook accounts, these are the SMTP Server settings:

  • SMTP Server: smtp.office365.com
  • SMTP Username: Complete Outlook email address
  • SMTP Password: Your Outlook password
  • SMTP Port: 587
  • SMTP TLS/SSL Required: Yes

Live or Hotmail SMTP Server Settings

For Live or Hotmail accounts, these are the SMTP Server settings:

  • SMTP Server: smtp.live.com
  • SMTP Username: Complete Live/Hotmail email address
  • SMTP Password: Your Windows Live Hotmail password
  • SMTP Port: 587
  • SMTP TLS/SSL Required: Yes

You need to search for its SMTP Server settings if you're using another email service. You now have everything you need to get started send email from ESP8266 NodeMCU board.

In your Arduino IDE, go to File > Examples > ESP-Mail-Client and play about with the examples—you'll need to enter your email data (sender and recipient accounts), the SMTP server settings, and your SSID and password.

Send an Email with HTML or Raw Text with ESP8266

The code below sends an email containing HTML or raw text over an SMTP server. The ESP8266, for example, sends an email once when it boots. The code should then be able to be modified and integrated into your own projects.

Don't upload the code just yet; you'll need to make some changes to make it work for you.

/*
  BokFive
  Complete project details at:
   - ESP32: https://bokfive.com/send-email-from-esp32-via-smtp-server/
   - ESP8266: https://bokfive.com/send-email-from-esp8266-nodemcu-via-smtp-server/
  
  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.
  Example adapted from: https://github.com/mobizt/ESP-Mail-Client
*/

// To send Emails using Gmail on port 465 (SSL), you need to create an app password: https://support.google.com/accounts/answer/185833

#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"

#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_PASS"

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

/* The SMTP Session object used for Email sending */
SMTPSession smtp;

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

void setup(){
  Serial.begin(115200);
  Serial.println();
  Serial.print("Connecting to AP");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(200);
  }
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  /** Enable the debug via Serial port
   * none debug or 0
   * basic debug or 1
  */
  smtp.debug(1);

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

  /* Declare the session config data */
  ESP_Mail_Session session;

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

  /* Declare the message class */
  SMTP_Message message;

  /* Set the message headers */
  message.sender.name = "ESP";
  message.sender.email = AUTHOR_EMAIL;
  message.subject = "ESP Test Email";
  message.addRecipient("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;*/

  /* Set the custom message header */
  //message.addHeader("Message-ID: <abcde.fghij@gmail.com>");

  /* Connect to server with the session config */
  if (!smtp.connect(&session))
    return;

  /* Start sending Email and close the session */
  if (!MailClient.sendMail(&smtp, &message))
    Serial.println("Error sending Email, " + smtp.errorReason());
}

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()){
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++){
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);
      time_t ts = (time_t)result.timestamp;
      localtime_r(&ts, &dt);

      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
      ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
      ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
    }
    Serial.println("----------------\n");
  }
}

As well as setting the sender email, SMTP Server details, recipient, and message, you will need to insert your network credentials.

How the Code Works

This code has been modified from a library example. The code in the example is well-commented, so you can understand what each line of code does. Let's take a look at the relevant parts that you need or may need to update.

First, insert your network credentials in the following lines:

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

Put in your SMTP server settings. If you're sending emails using a Gmail account, these are the settings:

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

Fill in the sender's email sign-in credentials (full email and APP password you already created).

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

Insert the recipient email:

#define RECIPIENT_EMAIL "RECIPIENTE_EMAIL@XXXX.com"

Set the message headers in setup() by including the following lines: sender name, sender email, email topic, and recipient name and email:

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

Set the message content (raw text) in the textMsg variable in the following lines:

//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;

You should insert your HTML line in the htmlMsg variable if you wish to transmit HTML text instead of plain text. Uncomment the following lines:

/*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;*/

Finally, the following lines send the message:

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

Demonstration

Transfer the code to your ESP8266. After uploading, open the serial monitor at 115200 baud.

If everything goes as planned, you should see a message similar to this in the Serial Monitor.

ESP8266 NodeMCU Send Email Message Successfully Serial Monitor

Check your email account. You should have received an email from your ESP8266 board.

Email received from ESP8266 Board Gmail Account

If the option to send a message using HTML text is set, the message will look like this:

ESP8266 SMTP Server Send Email with Body HTML text format

This is the email you should get if you enabled raw text messages.

ESP8266 SMTP Server Send Email with Body raw text format

Send Attachments via Email with ESP8266 (Arduino IDE)

This section will show you how to send attachments in your emails sent by the ESP8266. We’ll show you how to send .txt files or pictures. This can be useful to send a .txt file with sensor readings from the past few hours or other applications.

You should save the files you want to send in the ESP8266 filesystem (SPIFFS or LittleFS). The default filesystem used by the library is LittleFS. You can also send attachments saved on a microSD card, but we’ll not cover this subject in this tutorial.

Upload files to LittleFS

To send files via email, you should save them in the ESP8266 filesystem. The library uses LittleFS by default. To upload files to the LittleFS using Arduino IDE, you need to install a Filesystem Uploader Plugin. Read the following tutorial to learn how to install and upload files to the ESP8266 filesystem:

If you’re using VS Code + PlatformIO, follow the next tutorial to learn how to upload files to LittleFS:

Create a new Arduino sketch and save it. Go to Sketch Show Sketch folder. Inside the Arduino sketch folder, create a folder called data. Move a .png file and .txt file to your data folder.

Alternatively, you can click here to download the project folder.

Note: with the default code, your files must be named image.png and text_file.txt or you can modify the code to import files with a different name.

We’ll be sending these files:

Upload the files to filesystem to send as an email attachment

Your folder structure should look as follows (download project folder):

Send email attachments folder structure filesystem organizing files

After moving the files to the data folder, in your Arduino IDE, go to Tools ESP8266 LittleFS Data Upload, and wait for the files to be uploaded.

ESP8266 Tools LittleFS Data Upload Arduino IDE

You should get a success message on the debugging window. If the files were successfully uploaded, move on to the next section.

LittleFS image uploaded

Code

The following code sends an email with a .txt file and a picture attached. Before uploading the code, make sure you insert your sender email settings as well as your recipient email.

/** The smtp port e.g. 
 * 25  or esp_mail_smtp_port_25
 * 465 or esp_mail_smtp_port_465
 * 587 or esp_mail_smtp_port_587
*/
#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"

/* The SMTP Session object used for Email sending */
SMTPSession smtp;

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

void setup(){
  Serial.begin(115200);
  Serial.println();
  Serial.print("Connecting to AP");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(200);
  }
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  // Init filesystem
  ESP_MAIL_DEFAULT_FLASH_FS.begin();

  /** Enable the debug via Serial port
   * none debug or 0
   * basic debug or 1
  */
  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 = "mydomain.net";
  
  /*
  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 = 3;
  config.time.day_light_offset = 0;

  /* Declare the message class */
  SMTP_Message message;

  /* Enable the chunked data transfer with pipelining for large message if server supported */
  message.enable.chunking = true;

  /* Set the message headers */
  message.sender.name = "ESP Mail";
  message.sender.email = AUTHOR_EMAIL;

  message.subject = F("Test sending Email with attachments and inline images from Flash");
  message.addRecipient(F("Sara"), RECIPIENT_EMAIL);

  /** Two alternative content versions are sending in this example e.g. plain text and html */
  String htmlMsg = "This message contains attachments: image and text file.";
  message.html.content = htmlMsg.c_str();
  message.html.charSet = "utf-8";
  message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp;

  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal;
  message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;

  /* The attachment data item */
  SMTP_Attachment att;

  /** Set the attachment info e.g. 
   * file name, MIME type, file path, file storage type,
   * transfer encoding and content encoding
  */
  att.descr.filename = "image.png";
  att.descr.mime = "image/png"; //binary data
  att.file.path = "/image.png";
  att.file.storage_type = esp_mail_file_storage_type_flash;
  att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

  /* Add attachment to the message */
  message.addAttachment(att);

  message.resetAttachItem(att);
  att.descr.filename = "text_file.txt";
  att.descr.mime = "text/plain";
  att.file.path = "/text_file.txt";
  att.file.storage_type = esp_mail_file_storage_type_flash;
  att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

  /* Add attachment to the message */
  message.addAttachment(att);

  /* Connect to server with the session config */
  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 the Email and close the session */
  if (!MailClient.sendMail(&smtp, &message, true))
    Serial.println("Error sending Email, " + smtp.errorReason());
}

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()){
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++){
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);
      time_t ts = (time_t)result.timestamp;
      localtime_r(&ts, &dt);

      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
      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();
  }
}

How the code works

This code is very similar to the previous one, so we’ll just take a look at the relevant parts to send attachments.

In the setup(), you initialize the filesystem using the ESP Mail Client library method. The default filesystem set in the library for the ESP32 is LittleFS (you can change the default in the library file ESP_Mail_FS.h).

// Init filesystem
ESP_MAIL_DEFAULT_FLASH_FS.begin();

You need to create an attachment as follows:

/* The attachment data item */
SMTP_Attachment att;

Then, add the attachment details: filename, MIME type, file path, file storage type, and transfer encoding. In the following lines, we’re sending the image file.

att.descr.filename = "image.png";
att.descr.mime = "image/png"; 
att.file.path = "/image.png";
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

Finally, add the attachment to the message:


message.addAttachment(att);

If you want to send more attachments, you need to call the following line after adding the previous attachment:

message.resetAttachItem(att);

Then, enter the details of the other attachment (text file):

att.descr.filename = "text_file.txt";
att.descr.mime = "text/plain";
att.file.path = "/text_file.txt";
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

And add this attachment to the message:

message.addAttachment(att);

If you want to send more attachments, you need to call the following line before adding the next attachment:

message.resetAttachItem(att);

Then, enter the details of the other attachment (text file):

att.descr.filename = "text_file.txt";
att.descr.mime = "text/plain";
att.file.path = "/text_file.txt";
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

And add this attachment to the message:

message.addAttachment(att);

Finally, you just need to send the message as you did with the previous example:

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

Demonstration

After uploading the code, open the Serial Monitor at a baud rate of 115200. If everything goes as expected, you should get a similar message on the Serial Monitor.

ESP8266 NodeMCU Send Email Message Successfully Serial Monitor

If you go to your inbox, you should get the message with the two attachments:

Send Email with Attachments ESP8266 NodeMCU Gmail Account Arduino IDE

Conclusion

In this tutorial, you learned how to send emails with the ESP8266 NodeMCU board using an SMTP server. You've already learned how to send HTM text, raw text, and attachments.

Now, you're going to modify the code and include it in your own projects. For example, it may be useful to send a .txt file with the last hour's sensor readings, notifications when motion is detected, and much more.

If you like ESP8266, you may also like:

We hope you find this tutorial useful. Thanks for reading

Leave a Reply

Your email address will not be published. Required fields are marked *