Send emails with attachments (v2.x library): Arduino Ethernet – 1

Spread the love

I want to introduce version 2 of the EMailSender library, a significant evolution compared to version 1, with support for Arduino with w5100, w5200, and w5500 ethernet devices and enc28J60 clone devices, support for esp32 and esp8266 and finally (from version 2.2.0) support for all Arduino (SAMD) devices using the WiFiNINA library (Arduino UNO WiFi Rev.2, Arduino NANO 33 IoT, Arduino MKR 1010 and Arduino MKR VIDOR 4000 WiFi, etc.). You can now add attachments loaded from storage devices like SD or SPIFFS.

You can refer to version 1 of this article Send emails with esp8266 and Arduino

Introduction

A simple method to notify a problem is to use email, so I created a library to do It.

Send email with attachments Arduino library
Send an email with attachments Arduino library

This library uses the SMTP:

The Simple Mail Transfer Protocol (SMTP) is a communication protocol for electronic mail transmission. As an Internet standard, SMTP was first defined in 1982 by RFC 821 and updated in 2008 by RFC 5321 to Extended SMTP additions, which is the protocol variety in widespread use today. Mail servers and other message transfer agents use SMTP to send and receive mail messages. Proprietary systems such as Microsoft Exchange and IBM Notes and webmail systems such as Outlook.com, Gmail, and Yahoo! Mail may use non-standard protocols internally, but all use SMTP when sending to or receiving email from outside their own systems. SMTP servers commonly use the Transmission Control Protocol on port number 25. (cit WiKi)

Library

You can find my library here, or search for EMailSender on the Arduino IDE library manager.

To download.

Click the DOWNLOADS button in the top right corner, rename the uncompressed folder EMailSender.

Check that the EMailSender folder contains EMailSender.cpp and EMailSender.h.

Place the EMailSender library folder in your /libraries/ folder.

You may need to create the libraries subfolder if it’s your first library.

Restart the IDE.

Usage

Constructor: The default value is quite simple and uses Gmail as an SMTP server.

EMailSender emailSend("smtp.account@gmail.com", "password");

If you want to use another provider, you can use a more complex (but simple) constructor

	EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* name_from, const char* smtp_server, uint16_t smtp_port );
	EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* smtp_server, uint16_t smtp_port);
	EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* name_from );
	EMailSender(const char* email_login, const char* email_password, const char* email_from);
	EMailSender(const char* email_login, const char* email_password);

  • email_login: login to account SMTP
  • email_password: password account SMTP
  • email_from: email of the sender
  • name_from: the user name of the sender
  • smtp_server: server SMTP
  • smtp_port: SMTP port

You can also manage di parameter in realtime with these commands:

	void setSMTPPort(uint16_t smtp_port);
	void setSMTPServer(const char* smtp_server);
	void setEMailLogin(const char* email_login);
	void setEMailFrom(const char* email_from);
	void setNameFrom(const char* name_from);
	void setEMailPassword(const char* email_password);

Now respect to the v1.x library, you can note that isSecure flag no more exists

  • isSecure = false: if false BearSSL allows for ‘insecure’ TLS connections (only for >2.4.2 esp8266 core)

but you can manage It with

void setIsSecure(bool isSecure = false);

Other parameters that you can set post constructor are

void setUseAuth(bool useAuth = true);
void setPublicIpDescriptor(const char *publicIpDescriptor = "mischianti");

The first allows excluding the authentication handshake, the second change the HELO message identification.

Updated: 02/01/2021 (v2.1.5)

The other two additional configurations are

	void setEHLOCommand(bool useEHLO = false)
	void setSASLLogin(bool isSASLLogin = false)

The first change HELO command in EHLO, is needed by Postfix SMTP servers.

The second activates SASL login, so the login and password are sent all in one line.

You must connect to the internet :P.

Basic example with Gmail SMTP

You must refer to Part 2 to set up your Gmail account and check all parameters.

Create a message with the structure EMailMessage

    EMailSender::EMailMessage message;
    message.subject = "Subject";
    message.message = "Hi, How are you<br>Fine.";

Send message:

    EMailSender::Response resp = emailSend.send("account_to_send@gmail.com", message);

Then check the response:

    Serial.println("Sending status: ");
    Serial.println(resp.code);
    Serial.println(resp.desc);
    Serial.println(resp.status);

Example output:

Connection: ESTABLISHED
Got IP address: 192.168.1.104
Sending status: 
1
0
Message sent!

You can send a list of emails and select a CC or CCn. For example, send three email

    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
    EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);

or to 3 emails, the first as To and the last as CC

    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
    EMailSender::Response resp = emailSend.send(arrayOfEmail, 1, 2, message);

or first as To, second as CC, and third as CCn

    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
    EMailSender::Response resp = emailSend.send(arrayOfEmail, 1,1,1, message);

Arduino

Arduino usually manages a network with an external device. The standard device like w5100 uses the Ethernet library. The clones ENC28J60 have some libraries to select.

To select your device, you must go on EMailSenderKey.h library file and set the correct one

#define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_ENC28J60 // Default

The library loaded to manage this type of device is UIPEthernet. You can find the library on the library manager of the Arduino IDE

or you can change the default network type

#define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_W5100

This is the standard implementation and uses the Ethernet library.

An important thing to consider is that this Ethernet shield does not support SSL or TLS, so you must find a provider SMTP that offers an SMTP connection without this type of encryption.

I create a topic on the forum where you can add the provider you use, and you can find mine also.

SMTP server to use with the EMailSender library

As I already say, I use an ENC28J60 with Arduino Mega 2560. Here is the connection schema:

Arduino Mega And Enc28j60 Ethernet connection schema
Arduino Mega And Enc28j60 Ethernet connection schema

Send simple email

So here is an example of a simple EMail send with SendGrid provider.

  • apikey signals that we’re going to send a code related to an api module
  • your_password_apikey is the password given by Sendgrid when requesting an SMTP Relay key.
  • your_mail is the Sendgrid login address.
  • You must use 587 as port
/*
 * EMailSender library for Arduino, esp8266 and esp32
 * Arduino Mega and UIPEthernet send example with Sendgrid provider
 *
 * Pay attention you must set in the library
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_ENC28J60
 * for UIPEthernet
 *
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_W5100
 * for standard Ethernet
 *
 *
 * https://mischianti.org
 *
 */

#include "Arduino.h"
#include <SPI.h>
#include <UIPEthernet.h>

#include <EMailSender.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

EMailSender emailSend("<YOUR-SENDGRID-API-KEY>", "<YOUR-SENDGRID-PASSWD>", "<FROM-EMAIL>", "smtp.sendgrid.net", 587);

void setup()
{
    // Open serial communications and wait for port to open:
    Serial.begin(115200);
//    while (!Serial) {}

    delay(2000);

    Serial.println("Starting!");

    // start the Ethernet connection:
    if (Ethernet.begin(mac) == 0) {
      Serial.println("Failed to configure Ethernet using DHCP");
      while(1);
    }
    Serial.print("IP address ");
    Serial.println(Ethernet.localIP());


    EMailSender::EMailMessage message;
    message.subject = "Soggetto";
    message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";

    EMailSender::Response resp = emailSend.send("email_to_receive@gmail.com", message);

    Serial.println("Sending status: ");

    Serial.println(resp.status);
    Serial.println(resp.code);
    Serial.println(resp.desc);
}

void loop()
{

}

Send an email with attachments.

Send email with attachments Arduino esp8266 esp32 test with image and txt
Send an email with attachments Arduino esp8266 esp32 test with image and text.

I’m going to add this example only to explain how to do it, and I can’t test It because I don’t have a provider SMTP without SSL and TLS that support attachments.

Naturally, you need a storage device where you put the data to attach, and I use a standard SD adapter connected to CS pin 4.

You can find all information on connecting SD cards in this article, “How to use SD card with esp8266, esp32 and Arduino“.

Arduino Mega Enc28j60 SD connection schema EMailSender
Arduino Mega Enc28j60 SD connection schema EMailSender

The core of the code is the generation of structure to pass the information to load and stream the files.

    EMailSender::FileDescriptior fileDescriptor[1];
    fileDescriptor[0].filename = F("test.txt");
    fileDescriptor[0].url = F("/test.txt");
    fileDescriptor[0].mime = MIME_TEXT_PLAIN;
    fileDescriptor[0].encode64 = false;
    fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;

    EMailSender::Attachments attachs = {1, fileDescriptor};

    EMailSender::Response resp = emailSend.send("email_to_receive@gmail.com", message, attachs);

The fields EMailSender::FileDescriptior are pretty comprehensive but pay attention to encode64, which specifies if you want to encode all the content, this is useful if some special character is not supported.
For images, if you don’t specify the encode64 probably, they arrive with an artifact.

But pay attention; encoding a big file requires a bit of time.

The filed mime defines the mime type of the file you want to attach. Here are some examples of existing mime types.

Then you must select the storageType, in general, we only support SD and SPIFFS, for Arduino only SD.

The filename and url the filename is the name that appears to the receiver, and the URL is where the file is located in the FS selected.

Than in the EMailSender::Attachments you must specify the number of attachments and set the array of EMailSender::FileDescriptior.

The resulting code is this.

/*
 * EMailSender library for Arduino, esp8266 and esp32
 * Arduino Mega and UIPEthernet send example with attach
 * this example is not tested for all, I can't find a provider
 * that manage attach without SSL and TLS
 *
 * Pay attention you must set in the library
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_ENC28J60
 * for UIPEthernet
 *
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_W5100
 * for standard Ethernet
 *
 * The base64 encoding of the image is slow, so be patient
 *
 *
 * https://mischianti.org
 *
 */

#include "Arduino.h"
#include <SPI.h>
#include <UIPEthernet.h>
#include <SD.h>

#include <EMailSender.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

//	EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* smtp_server, uint16_t smtp_port, bool isSecure = false);
EMailSender emailSend("<LOGIN>", "<PASSWD>", "<EMAIL-FROM>", "<SMTP-SERVER>", "<SMTP-SERVER-PORT>");

void printDirectory(File dir, int numTabs);

//The setup function is called once at startup of the sketch
void setup()
{
    Serial.begin(115200);

    delay(2000);

    Serial.println("Starting!");

    Serial.print("Initializing SD card...");

    if (!SD.begin(4)) {
      Serial.println("initialization failed!");
      while (1);
    }
    Serial.println("initialization done.");

    File root = SD.open("/");

    printDirectory(root, 0);

    Serial.println("done!");

//	File myFile = SD.open("/TEST.TXT", "r");
//	  if(myFile) {
//		  myFile.seek(0);
//		  DEBUG_PRINTLN(F("OK"));
//		myFile.close();
//	  }
//	  else {
//		  DEBUG_PRINTLN(F("KO"));
//	  }
//
//
    // start the Ethernet connection:
    if (Ethernet.begin(mac) == 0) {
      Serial.println("Failed to configure Ethernet using DHCP");
      while(1);
    }
    Serial.print("IP address ");
    Serial.println(Ethernet.localIP());


    EMailSender::EMailMessage message;
    message.subject = "Soggetto";
    message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";
    message.mime = MIME_TEXT_PLAIN;

// 		Two file
//    EMailSender::FileDescriptior fileDescriptor[2];
//    fileDescriptor[1].filename = F("test.txt");
//    fileDescriptor[1].url = F("/test.txt");
//    fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
//
//    fileDescriptor[0].filename = F("logo.jpg");
//    fileDescriptor[0].url = F("/logo.jpg");
//    fileDescriptor[0].mime = "image/jpg";
//    fileDescriptor[0].encode64 = true;
//    fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
//
//    EMailSender::Attachments attachs = {2, fileDescriptor};

// 		One file
    EMailSender::FileDescriptior fileDescriptor[1];
    fileDescriptor[0].filename = F("test.txt");
    fileDescriptor[0].url = F("/test.txt");
    fileDescriptor[0].mime = MIME_TEXT_PLAIN;
    fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;

    // Pay attention base64 encoding is quite slow
//	EMailSender::FileDescriptior fileDescriptor[2];
//	fileDescriptor[0].filename = F("logo.jpg");
//	fileDescriptor[0].url = F("/logo.jpg");
//	fileDescriptor[0].mime = "image/jpg";
//	fileDescriptor[0].encode64 = false;
//	fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;

    EMailSender::Attachments attachs = {1, fileDescriptor};

    EMailSender::Response resp = emailSend.send("email_to_receive@gmail.com", message, attachs);

    Serial.println("Sending status: ");

    Serial.println(resp.status);
    Serial.println(resp.code);
    Serial.println(resp.desc);

    File root2 = SD.open("/");

    printDirectory(root2, 0);

    Serial.println("done!");
    SD.end();
}

void loop()
{

}

void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

Email to a distribution list

From version 2.1.1 distribution list is supported, so you can send emails to multiple accounts and select if you want to send as To, CC or CCn.

Here an example

/*
 * EMailSender library for Arduino, esp8266 and esp32
 * Arduino Mega and UIPEthernet send example with Sendgrid provider
 * to a distribution list
 *
 * Pay attention you must set in the library
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_ENC28J60
 * for UIPEthernet
 *
 * #define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO 	NETWORK_W5100
 * for standard Ethernet
 *
 *
 * https://mischianti.org
 *
 */

#include "Arduino.h"
#include <SPI.h>
#include <UIPEthernet.h>

#include <EMailSender.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

EMailSender emailSend("<YOUR-SENDGRID-API-KEY>", "<YOUR-SENDGRID-PASSWD>", "<FROM-EMAIL>", "smtp.sendgrid.net", 25);

void setup()
{
    // Open serial communications and wait for port to open:
    Serial.begin(115200);
//    while (!Serial) {}

    delay(2000);

    Serial.println("Starting!");

    // start the Ethernet connection:
    if (Ethernet.begin(mac) == 0) {
      Serial.println("Failed to configure Ethernet using DHCP");
      while(1);
    }
    Serial.print("IP address ");
    Serial.println(Ethernet.localIP());


    EMailSender::EMailMessage message;
    message.subject = "Soggetto";
    message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";

    // Send to 3 different email
    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
    EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);

//    // Send to 3 different email, 2 in C and 1 in CC
//    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
//    EMailSender::Response resp = emailSend.send(arrayOfEmail, 2, 1, message);
//
//    // Send to 3 different email first to C second to CC and third to CCn
//    const char* arrayOfEmail[] = {"<FIRST>@gmail.com", "<SECOND>@yahoo.com", "<THIRD>@hotmail.com"};
//    EMailSender::Response resp = emailSend.send(arrayOfEmail, 1,1,1, message);

    Serial.println("Sending status: ");

    Serial.println(resp.status);
    Serial.println(resp.code);
    Serial.println(resp.desc);
}

void loop()
{

}

How to use a Gmail account

By default, you can’t use a Gmail SMTP account, and there are two ways to activate these features.

Allow less secure apps to access your Gmail account

Google may block sign-in attempts from some apps or devices that do not use modern security standards. Since these apps and devices are easier to break into, blocking them helps keep your account safe.

To disable this security feature:

  1. Sign in to Gmail
  2. Click here to access Less Secure App Access in My Account.
  3. Next to “Allow less secure apps: OFF,” select the toggle switch to turn ON.

This setting may not be available for:

For G-Suite users: Enabling less secure apps to access accounts (deprecated)

  1. Sign in to your Google Admin console (Sign in using an administrator account, not your current account.
  2. Click Security> Basic settings.
  3. Under Less secure apps, select Go to settings for less secure apps.
  4. Select the Allow users to manage their access to a less secure apps radio button in the subwindow.

Once you’ve set Allow users to manage their access to less secure apps to on, affected users within the selected group or Organizational Unit will be able to toggle access for less secure apps on or off themselves.

G-Suite admins: Enforcing access to less secure apps for all users

Use this setting when you want to ensure that access by a less secure app is available to all for a limited time, such as for upgrades.

  1. Sign in to your Google Admin console. Sign in using an administrator account
  2. Click Security> Basic settings.
  3. Under Less secure apps, select Go to settings for less secure apps.
  4. In the subwindow, select the Enforce access to less secure apps for all users radio button.

Once you’ve set Enforce access to less secure apps for all users to on, affected users within the selected group or Organizational Unit will not be able to toggle access for less secure apps off themselves. You will have to set the setting back to Allow users to manage their access to less secure apps to allow them to toggle access for less secure apps on or off themselves.

Create an application password

  1. Go to the Gmail security settings page: https://myaccount.google.com/security
  2. First, you must enable 2-factor identification.
  3. Then you need to create a password for this application. The password is a 16-character string, copy it and use it as your regular SMTP Gmail account password!

Thanks

  1. Send emails with attachments (EMailSender v2.x library): Arduino Ethernet
  2. Send emails with attachments (EMailSender v2.x library): esp32 and esp8266
  3. Send emails with attachments (EMailSender v2.x library): Arduino SAMD boards (WiFiNINA)
  4. Send emails with attachments and SSL (like Gmail): STM32, w5500, enc28j60, SD, and SPI Fash


Spread the love

20 Responses

  1. Gilles Mangin-Voirin says:

    Hello, thanks for the great job.
    After a lot of testing I finally managed to get the code running correctly on Arduino with shield W5100. (smtp.orange.fr)
    The mails are sent correctly.
    I received the email on my ztu.gmail.com mailbox but when trying to receive it on an xyz@wanadoo.fr address I received an email on my sending mailbox:
    host back1g-mail01-041.me-wanadoo.net [10.232.119.68] said: 554 5.6.0 Message contains invalid header.

    After a lot of research (not very successful) I found this:
    1) It is absolutely necessary to put an empty line between the subject and the body of the message!

    Anyone know how to do this?

    2) Put the recipient first …

    Or in any case solve my problem of shipping email to an orange.fr address
    Thank you

  2. Gilles says:

    Hello, while waiting for a solution to my mail problem with orange, I am working on sending mail with gmail.
    I’m trying to put your program in a function to integrate it into a connected hive program …
    When I solicit this function (sending email) at the end of sending the main loop stops …

    […]

  3. jeff says:

    You library is not support STARTTLS right?

  4. Gilles says:

    I hope to be in the right place to comment on the function :
    EMailSender :: FileDescriptior fileDescriptor [attached_piece];

    This one does not accept attached_piece = 0: that stops the sketch!

    I don’t know if it’s a bug or if it’s intended …
    It is true that doing a function to attach a file and ultimately not attach a file is not too logical on my part, but it was to avoid creating two functions for sending email:
    one with attachment and the other just an email without attachment …

    If anyone knows how to get it to work

    EMailSender :: FileDescriptior fileDescriptor [0]; I am interested!

    Thank you

  5. dav id says:

    Hello
    thank you, is it possible to send a mail for instance each day with message like temperature of ds18b20 ? thank you

  6. dav id says:

    Hi
    thank you for your quickly answer. In fact i notice that “message” is const string, so it ‘s not possible to change it.

      EMailSender::EMailMessage message;
        message.subject = "titre 1";
        message.message = "message 1";
        EMailSender::Response resp = emailSend.send("toto@free.fr", message);
        
        delay (10000);  
        message.subject = "titre 2";
        message.message = "message 2";
        EMailSender::Response resp = emailSend.send("toto@free.fr", message);
    

    it s not possible to compile
    “exit status 1
    redeclaration of ‘EMailSender::Response resp'”

    I would like to have because :
    one ds18b20 is connected with my 8266 and i want that 8266 will mesure temperature and send a email each day.
    So temperature change each days of course.

    sorry if i a m not clear

    Thank you

    • Hi David,
      now i understand, sorry for the previous response, the problem is that you redeclare the structure, you must only do this

        EMailSender::EMailMessage message;
        message.subject = "titre 1";
        message.message = "message 1";
        EMailSender::Response resp = emailSend.send("toto@free.fr", message);
         
        delay (10000);  
        message.subject = "titre 2";
        message.message = "message 2";
        resp = emailSend.send("toto@free.fr", message);
      

      Remove the second declaration of resp.

      I think this is what you need.

  7. sspence65 says:

    I’m using a ESP32 with a W5500 (not using wifi). The example sketch causes the esp32 to repeatedly reboot:

    /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1442 (xQueueGenericReceive)- assert failed!
    abort() was called at PC 0x40088301 on core 1

  8. Fernando Antonio Camargo says:

    Gentleman, I really liked your job.
    I would like to request your help to use the RP2040 + W5500 just to send an email without SSL (for now).
    I am planning to handle the SSL Certificates later, that will be stored in EEPROM.
    Can you help me?

  9. Lukasz says:

    what about using nucleo with ethernet instead of w5500?

Leave a Reply

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