Projetos

SMTP: Enviando e-mail com ESP32

Eletrogate 19 de abril de 2022

Introdução

Neste post, será mostrado como enviar e-mail’s com o ESP32 através de um servidor SMTP, utilizando serviços do Gmail (Google), contendo mensagem de texto ou de HTML e anexos. Também será abordado um projeto de envio de e-mail de alerta quando uma porta for aberta.


SMTP

SMTP, acrônimo de Simple Mail Transfer Protocol (Protocolo de transferência de correio simples), é um protocolo de servidor para envio de mensagens de e-mail através da Internet entre dois dispositivos computacionais. O SMTP tem a função somente de envio. Por isso, não permite que um usuário descarregue/solicite as mensagens de um servidor. Assim, para a leitura, é necessário o uso de um software cliente de e-mail com suporte ao protocolo de leitura POP ou IMAP.


Configurar SMTP Gmail

Para permitir que o ESP32 envie e-mail’s utilizando uma conta Gmail do Google, é necessário, primeiro, se ter uma conta Gmail.

Nota: Não é recomendável utilizar uma conta pessoal para o envio de e-mail’s pelo ESP32. Caso extrapole o limite de envios de e-mail’s (500 e-mails em um dia), sua conta pode ser bloqueada para fazer novos envios de e-mail’s de 1 a 24 horas como uma medida para evitar spam e manter as contas seguras.

Criar uma nova conta

Para criar uma nova conta Gmail, acesse google.com/gmail/about e clique em ‘Crie uma conta’;

Siga as etapas na tela para configurar sua conta e conclua a criação de conta Gmail.

Ativar Verificação em duas etapas

Para permitir que o ESP32 acesse sua Conta do Google, é necessário ter a funcionalidade ‘senhas de app‘. Para ativar as senhas de app, a verificação em duas etapas deve estar ativa.

Em seguida, use a conta que você criou para acessar o Gerenciador de Conta Google através do endereço myaccount.google.com. No painel de navegação (localizado na lateral esquerda ou superior), clique em Segurança

Em seguida, na seção Como fazer login no Google, clique em Verificação em duas etapas;

Clique em ‘Primeiros Passos’, e siga as etapas exibidas na tela.

Criar Senhas de app

Em Gerenciador de Conta Google, no painel de navegação, clique em Segurança. Em seguida, na seção Como fazer login no Google, clique em Senhas de app;

Na parte inferior da próxima tela:

  • em Selecionar app (1) escolha E-mail;
  • em Selecionar dispositivo (2) escolha Outro (nome personalizado) e defina um nome como, por exemplo, ‘ESP32’;

Após configurar, clique em Gerar (3):

Após gerar a Senha de app, anote a senha de 16 caracteres gerada, pois iremos utilizar no sketch de envio de e-mail com ESP32.

Configurações do cliente de e-mail SMTP do Gmail

  • Servidor: smtp.gmail.com;
  • Requer SSL: Sim;
  • Requer TLS: Sim (se disponível);
  • Requer autenticação: Sim;
  • Porta para SSL: 465;
  • Porta para TLS/STARTTLS: 587;
  • Nome de exibição: Seu nome;
  • Endereço de e-mail: Seu endereço de e-mail completo;
  • Senha: Sua Senha de app criado anteriormente;

Biblioteca de Envio de Email com ESP32

Para o envio de e-mail com o ESP32, é necessário se ter a biblioteca ESP Mail Client na Arduino IDE. Para instalá-la na Arduino IDE:

  • vá em Ferramentas → Gerenciar Bibliotecas… 
  • Na barra de pesquisa, pesquise ESP Mail Client e instale a biblioteca ESP Mail Client com autor Mobizt.

Esta biblioteca permite o envio de e-mail com vários anexos suportados e oferece mais confiabilidade e flexibilidade de usos. Além de suportar ESP32 e ESP8266 da Espressif, também suporta placas Ethernet SPI W5100,W5500 e ENC28J60, que podem ser conectadas à qualquer versão de Arduino que suporte comunicação SPI.

Mais detalhes sobre a biblioteca, você encontra em github.com/mobizt/ESP-Mail-Client.


Algoritmos de Envio de E-mail com ESP32

Para o Funcionamento dos seguintes algoritmos, faça as seguintes substituições:

  • <O_SSID_de_sua_rede_WiFi> para o SSID de sua rede WiFi;
  • <A_senha_de_sua_rede_WiFi> para a senha de sua rede WiFi;
  • <O_email_remetente> para o seu e-mail cadastrado em Senhas de app;
  • <A_senha_de_app_do_email_remetente> para a Senha de app do e-mail;
  • <O_email_destinatario> para o endereço de e-mail ao qual irá enviar o e-mail;

Algoritmo de envio do e-mail com corpo de texto simples

O seguinte algoritmo envia um e-mail com o corpo de texto com texto simples.

/*******************************************************************************
            Algoritmo de envio de e-mail com texto simples com ESP32
                                Sketch Principal

                            Criado em 01 abr. 2022
                               por Michel Galvão

  Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits
                            https://www.eletrogate.com/
*******************************************************************************/

// Inclusão da(s) biblioteca(s)
#include <WiFi.h> // Biblioteca nativa do ESP32
#include <ESP_Mail_Client.h>  // Biblioteca de cliente de e-mail do 
//                                Arduino para enviar.
//                                Referência: https://github.com/mobizt/ESP-Mail-Client

// Configurações da rede WiFi à se conectar
#define WIFI_SSID "<O_SSID_de_sua_rede_WiFi>"
#define WIFI_PASSWORD "<A_senha_de_sua_rede_WiFi>"

// Configurações do SMTP host
#define SMTP_HOST "smtp.gmail.com" // SMTP host
#define SMTP_PORT 465 // SMTP port

// As credenciais de login
#define AUTOR_EMAIL "<O_email_remetente>"
#define AUTOR_SENHA "<A_senha_de_app_do_email_remetente>"

// O objeto de sessão SMTP usado para envio de e-mail
SMTPSession smtp;

// Protótipos das Funções
void smtpCallback(SMTP_Status status);
bool enviaEmail_TXT(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageTXT,
                    String stmpHost,
                    int stmpPort);

void setup() {
  // Inicia Serial
  Serial.begin(115200);
  Serial.println();
  delay(1000);

  // Inicia conexão WiFi
  Serial.println("Conectando à rede WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) // Enquanto o status de conexão ao WiFi
    //                                      não for bem sucedido, ...
  {
    Serial.print(".");
    delay(200);
  }

  // Mostra na Serial que a conexão está realizada e mostra o IP do ESP32 na rede
  Serial.println("\nWiFi conectado.");

  // Habilita a depuração via porta serial:
  //    0: nenhuma depuração
  //    1: depuração básica
  smtp.debug(0);

  // Define a função de retorno de chamada para obter os resultados de envio
  smtp.callback(smtpCallback);

  //Define a mensagem de texto a ser enviada
  String messageTXT = "Olá mundo! Teste de envio de E-mail com somente texto.";
  // função que envia o e-mail
  enviaEmail_TXT("Michel Galvão",
                 AUTOR_EMAIL,
                 AUTOR_SENHA,
                 "Teste de E-mail",
                 "Michel Galvão",
                 "<O_email_destinatario>",
                 messageTXT,
                 SMTP_HOST,
                 SMTP_PORT);
  while (1); // loop infinito

}

void loop() {}

/**
  Envia um e-mail para um destinatário.

  @param nomeRemetente - o nome ao qual será mostrado do remetente
  @param emailRemetente - o email do remetente
  @param senhaRemetente - a senha criada em Senhas de app
  @param assunto - o assunto do e-mail
  @param nomeDestinatario - o nome do destinatário
  @param emailDestinatario - o email do destinatário
  @param messageTXT - a mensagem de texto do e-mail
  @param stmpHost - o servidor SMTP
  @param stmpPort - a porta SSL do servidor SMTP

  @return - true se envio foi bem sucedido ou false se houve alguma falha
*/
bool enviaEmail_TXT(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageTXT,
                    String stmpHost,
                    int stmpPort) {
                      
  // Objeto para declarar os dados de configuração da sessão
  ESP_Mail_Session session;
  // Defina os dados de configuração da sessão
  session.server.host_name = stmpHost;
  session.server.port = stmpPort;
  session.login.email = emailRemetente;
  session.login.password = senhaRemetente;
  session.login.user_domain = "";
  // Defina o tempo de configuração do NTP
  session.time.ntp_server = F("time.google.com"); // Utilizado o NTP do Google:
  //                                                  https://developers.google.com/time
  session.time.gmt_offset = -3; // define o deslocamento em segundos 
  //                                do fuso horário local em relação ao
  //                                GMT do Meridiano de Greenwich.
  
  session.time.day_light_offset = 0; // define o deslocamento em segundos do 
  //                                      fuso horário local. Este valor costuma
  //                                      ser 3600 para horário de verão +1h ou 
  //                                      0 para fusos sem horário de verão.

  // Instanciação do objeto da classe de mensagem
  SMTP_Message message;

  // Definição os cabeçalhos das mensagens
  message.sender.name = nomeRemetente;
  message.sender.email = emailRemetente;
  message.subject = assunto;
  message.addRecipient(nomeDestinatario, emailDestinatario);
  message.text.content = messageTXT.c_str();

  // O conjunto de caracteres de mensagem de texto html, por exemplo:
  //  us-ascii
  //  utf-8
  //  utf-7
  // O valor padrão é utf-8
  message.text.charSet = "utf-8";

  // A codificação de transferência de conteúdo. Ex:
  //  enc_7bit ou "7bit" (não codificado)
  //  enc_qp ou "quoted-printable" (codificado)
  //  enc_base64 ou "base64" (codificado)
  //  enc_binary ou "binary" (não codificado)
  //  enc_8bit ou "8bit" (não codificado)
  //  O valor padrão é "7bit"
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  // A prioridade da mensagem:
  //  esp_mail_smtp_priority_high ou 1
  //  esp_mail_smtp_priority_normal ou 3
  //  esp_mail_smtp_priority_low ou 5
  //  O valor padrão é esp_mail_smtp_priority_low
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;

  // As notificações de status de entrega, Ex:
  //  esp_mail_smtp_notify_never
  //  esp_mail_smtp_notify_success
  //  esp_mail_smtp_notify_failure
  //  esp_mail_smtp_notify_delay
  //  O valor padrão é esp_mail_smtp_notify_never
  message.response.notify = esp_mail_smtp_notify_success |
                            esp_mail_smtp_notify_failure |
                            esp_mail_smtp_notify_delay;

  // Conecte-se ao servidor com a configuração da sessão
  if (!smtp.connect(&session))
    return false;

  // Começa a enviar e-mail e fecha a sessão
  if (!MailClient.sendMail(&smtp, &message)) {
    Serial.println("Erro ao enviar e-mail, " + smtp.errorReason());
    return false;
  }
  return true;
}

/** 
 *  Função de retorno de chamada para obter o status de envio de e-mail 
*/
void smtpCallback(SMTP_Status status) {
  // Imprime o status atual
  Serial.println(status.info());

  // Imprima o resultado do envio
  if (status.success()) {
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Mensagem enviada com sucesso: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Falha na mensagem enviada: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
      /* Obter o item de resultado */
      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 ? "sucesso" : "fracassado");
      ESP_MAIL_PRINTF("Data/Hora: %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("Recebedor: %s\n", result.recipients);
      ESP_MAIL_PRINTF("Sujeito: %s\n", result.subject);
    }
    Serial.println("----------------\n");
  }
}

Explicação do Código

Primeiro, fazemos a inclusão das bibliotecas.

Em seguida, definimos o SSID e a senha da rede WiFi. Também definimos o endereço do host e a porta do host SMTP. Em seguida, definimos o endereço e a senha do e-mail que será o remetente.

Logo após, criamos o objeto da classe SMTPSession. Este objeto será usado para o envio de e-mail.

Logo abaixo, criamos os protótipos das funções utilizados no algoritmo.

Em void setup(), iniciamos o WiFi com as credenciais de rede. Também entramos em uma estrutura de repetição para esperarmos o ESP32 conectar na rede WiFi.

Em seguida, ainda em void setup(), há a opção de habilitar a depuração via Serial. Para habilitar, basta alterar o parâmetro para 1. Também definimos a função que será usada para callback dos resultados de envio do e-mail.

Seguidamente, definimos a mensagem de texto que será o corpo de texto do e-mail. Com o corpo de texto definido, chamamos a função responsável pelo envio do e-mail. Após o envio do e-mail, entramos em um loop infinito, já que no void loop(), não fazemos mais nada.

A função bool enviaEmail_TXT(), é responsável pelo envio de e-mail para um único destinatário especificado nos parâmetros da função. Esta função retorna true se o envio for bem sucedido e false se ocorreu algum erro no envio. Nesta função, devemos passar os parâmetros listados na imagem abaixo:

Dentro desta função, criamos um objeto da classe ESP_Mail_Session, responsável por configurar os dados da sessão de envio do e-mail. Estes dados são: o nome do host SMTP, a porta do host SMTP, o e-mail do remetente e a senha do e-mail do remetente.

Também definimos as configurações do servidor NTP. Estas configurações podem ser vistas com mais detalhes no post Salvando preferências no ESP32, aqui mesmo, no blog eletrogate.

É definido, em seguida, o objeto da classe SMTP_Message, que é responsável por definir os dados da mensagem do e-mail. Estes dados incluem o nome do remetente, o e-mail do remetente, o assunto do e-mail, o nome e o e-mail do destinatário e o texto do corpo do e-mail. Outros dados definidos estão detalhados abaixo.

  • Definimos a codificação de caracteres do e-mail, que neste casso utilizamos a UTF-8.
  • Definimos a codificação de transferência de conteúdo para 7 bit (não codificado).
  • Definimos a prioridade de e-mail para baixa.
  • Definimos as notificações de status de entrega para que ocorram quando haver sucesso OU falha OU atraso.

Após as definições de dados da mensagem do e-mail, conectamo-nos ao servidor SMTP com as configurações de sessão definidas anteriormente. Caso haja falha na conexão, retornamos false para a chamada da função.

Caso, na conexão, não tenha ocorrido nenhuma falha, enviamos o e-mail e fechamos a sessão. Se ocorreu algum erro, também retornamos false para a chamada da função.

No final da função, retornamos true para a chamada da função.

A função de callback stmpCallback mostra na Serial o status após o envio do e-mail (se foi enviado com sucesso ou houve falha).

Ao executar o programa no ESP32, a saída do Monitor Serial é:

20:17:44.371 -> Conectando à rede WiFi
20:17:44.473 -> ................
20:17:47.664 -> WiFi conectado.
20:17:47.664 -> Acquiring time from NTP server...
20:17:47.698 -> 
20:17:48.310 -> Connecting to SMTP server...
20:17:50.245 -> 
20:17:50.245 -> SMTP server connected, wait for greeting...
20:17:50.279 -> 
20:17:50.279 -> Sending greeting response...
20:17:50.483 -> 
20:17:50.483 -> Logging in...
20:17:50.824 -> 
20:17:50.824 -> Sending Email...
20:17:50.824 -> 
20:17:50.824 -> Sending message header...
20:17:51.367 -> 
20:17:51.367 -> Sending message body...
20:17:51.673 -> 
20:17:51.673 -> Finishing the message sending...
20:17:52.387 -> 
20:17:52.387 -> Closing the session...
20:17:52.795 -> 
20:17:52.795 -> Message sent successfully
20:17:52.795 -> 
20:17:52.795 -> ----------------
20:17:52.795 -> Mensagem enviada com sucesso: 1
20:17:52.829 -> Falha na mensagem enviada: 0
20:17:52.829 -> ----------------
20:17:52.829 -> 
20:17:52.829 -> Message No: 1
20:17:52.829 -> Status: sucesso
20:17:52.829 -> Data/Hora: 2022/4/1 20:17:56
20:17:52.829 -> Recebedor: |⸮⸮?
20:17:52.829 -> Sujeito: {⸮?
20:17:52.829 -> ----------------
20:17:52.829 -> 

No Gmail do endereço do destinatário, é recebido o e-mail enviado pelo ESP32:

Algoritmo de envio do e-mail com corpo de texto HTML

O seguinte algoritmo envia um e-mail com o corpo de texto com HTML.

/*******************************************************************************
            Algoritmo de envio de e-mail com corpo de texto HTML com ESP32
                                Sketch Principal

                            Criado em 01 abr. 2022
                               por Michel Galvão

  Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits
                            https://www.eletrogate.com/
*******************************************************************************/

// Inclusão da(s) biblioteca(s)
#include <WiFi.h> // Biblioteca nativa do ESP32
#include <ESP_Mail_Client.h>  // Biblioteca de cliente de e-mail do 
//                                Arduino para enviar.
//                                Referência: https://github.com/mobizt/ESP-Mail-Client

// Configurações da rede WiFi à se conectar
#define WIFI_SSID "<O_SSID_de_sua_rede_WiFi>"
#define WIFI_PASSWORD "<A_senha_de_sua_rede_WiFi>"

// Configurações do SMTP host
#define SMTP_HOST "smtp.gmail.com" // SMTP host
#define SMTP_PORT 465 // SMTP port

// As credenciais de login
#define AUTOR_EMAIL "<O_email_remetente>"
#define AUTOR_SENHA "<A_senha_de_app_do_email_remetente>"

// O objeto de sessão SMTP usado para envio de e-mail
SMTPSession smtp;

// Protótipos das Funções
void smtpCallback(SMTP_Status status);
bool enviaEmail_HTML(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageHTML,
                    String stmpHost,
                    int stmpPort);;

void setup() {
  // Inicia Serial
  Serial.begin(115200);
  Serial.println();
  delay(1000);

  // Inicia conexão WiFi
  Serial.println("Conectando à rede WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) // Enquanto o status de conexão ao WiFi
    //                                      não for bem sucedido, ...
  {
    Serial.print(".");
    delay(200);
  }

  // Mostra na Serial que a conexão está realoizado e mostra o IP do ESP32 na rede
  Serial.println("\nWiFi conectado.");

  // Habilita a depuração via porta serial:
  //    0: nenhuma depuração
  //    1: deburação básica
  smtp.debug(0);

  // Define a função de retorno de chamada para obter os resultados de envio
  smtp.callback(smtpCallback);

  //Define a mensagem de texto à ser enviada
  String messageHTML = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
</head>
<body style="background-color: rgb(250, 241, 192);">
<h1>Teste de HTML!</h1>
<p>Este &eacute; um corpo de texto criado em HTML.</p>
<h3>Exemplos de funcionalidades</h3>
<ol>
<li>
Exemplo de link: <a href="https://google.com.br" target="_blank" rel="noopener">
google.com.br</a>
</li>
<li>Exemplo de Tabela:</li>
</ol>
<table style="border-collapse: collapse; width: 13.0593%; height: 37px;" border="1">
<tbody>
<tr>
<td style="width: 33.3333%;">Coluna 1</td>
<td style="width: 33.3333%;">Coluna 2</td>
</tr>
<tr>
<td style="width: 33.3333%;">1.1</td>
<td style="width: 33.3333%;">1.2</td>
</tr>
</tbody>
</table>
<ul>
<li>
Exemplo de imagem:&nbsp;
<img src=
"https://blog.eletrogate.com/wp-content/themes/blog-eletrogate/dist/img/image/logo.png"
alt="" width="484" height="52" />
</li>
</ul>
<p>&nbsp;</p>
<ul>
<li>
Exemplo de <span style="font-family: 'comic sans ms', sans-serif;">
<span style="background-color: #eccafa;">Formata&ccedil;&atilde;o</span>
&nbsp;de <span style="color: #169179;">Texto</span>
</span>
: <strong>Negrito</strong>,
<em>It&aacute;lico</em>,
<span style="text-decoration: underline;">Sublinhado</span>,
<span style="text-decoration: line-through;">Tachado</span>,
x<sup>2</sup>, H<sub>2</sub>O, <code>CODE;</code>, 😄
</li>
</ul>
<p>&nbsp;</p>
</body>
</html>
)rawliteral"; // "Fim de raw text
  // função que envia o e-mail
  enviaEmail_HTML("Michel Galvão",
                 AUTOR_EMAIL,
                 AUTOR_SENHA,
                 "Teste de E-mail com HTML",
                 "Michel Galvão",
                 "<O_email_destinatario>",
                 messageHTML,
                 SMTP_HOST,
                 SMTP_PORT);
  while (1); // loop infinito

}

void loop() {}

/**
  Envia um e-mail para um destinatário.

  @param nomeRemetente - o nome ao qual será mostrado do remetente
  @param emailRemetente - o email do remetente
  @param senhaRemetente - a senha criada em Senhas de app
  @param assunto - o assunto do e-mail
  @param nomeDestinatario - o nome do destinatário
  @param emailDestinatario - o email do destinatário
  @param messageTXT - a mensagem de texto do e-mail
  @param stmpHost - o servidor SMTP
  @param stmpPort - a porta SSL do servidor SMTP

  @return - true se envio foi bem sucedido ou false se houve alguma falha
*/
bool enviaEmail_HTML(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageHTML,
                    String stmpHost,
                    int stmpPort) {
                      
  // Objeto para declarar os dados de configuração da sessão
  ESP_Mail_Session session;
  // Defina os dados de configuração da sessão
  session.server.host_name = stmpHost;
  session.server.port = stmpPort;
  session.login.email = emailRemetente;
  session.login.password = senhaRemetente;
  session.login.user_domain = "";
  // Defina o tempo de configuração do NTP
  session.time.ntp_server = F("time.google.com"); // Utilizado o NTP do Google:
  //                                                  https://developers.google.com/time
  session.time.gmt_offset = -3; // define o deslocamento em segundos 
  //                                do fuso horário local em relação ao
  //                                GMT do Meridiano de Greenwich.
  
  session.time.day_light_offset = 0; // define o deslocamento em segundos do 
  //                                      fuso horário local. Este valor costuma
  //                                      ser 3600 para horário de verão +1h ou 
  //                                      0 para fusos sem horário de verão.

  // Instanciação do objeto da classe de mensagem
  SMTP_Message message;

  // Definição os cabeçalhos das mensagens
  message.sender.name = nomeRemetente;
  message.sender.email = emailRemetente;
  message.subject = assunto;
  message.addRecipient(nomeDestinatario, emailDestinatario);
  message.html.content = messageHTML.c_str();

  // O conjunto de caracteres de mensagem de texto html, por exemplo:
  //  us-ascii
  //  utf-8
  //  utf-7
  // O valor padrão é utf-8
  message.text.charSet = "utf-8";

  // A codificação de transferência de conteúdo. Ex:
  //  enc_7bit ou "7bit" (não codificado)
  //  enc_qp ou "quoted-printable" (codificado)
  //  enc_base64 ou "base64" (codificado)
  //  enc_binary ou "binary" (não codificado)
  //  enc_8bit ou "8bit" (não codificado)
  //  O valor padrão é "7bit"
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  // A prioridade da mensagem:
  //  esp_mail_smtp_priority_high ou 1
  //  esp_mail_smtp_priority_normal ou 3
  //  esp_mail_smtp_priority_low ou 5
  //  O valor padrão é esp_mail_smtp_priority_low
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;

  // As notificações de status de entrega, Ex:
  //  esp_mail_smtp_notify_never
  //  esp_mail_smtp_notify_success
  //  esp_mail_smtp_notify_failure
  //  esp_mail_smtp_notify_delay
  //  O valor padrão é esp_mail_smtp_notify_never
  message.response.notify = esp_mail_smtp_notify_success |
                            esp_mail_smtp_notify_failure |
                            esp_mail_smtp_notify_delay;

  // Conecte-se ao servidor com a configuração da sessão
  if (!smtp.connect(&session))
    return false;

  // Começa a enviar e-mail e fecha a sessão
  if (!MailClient.sendMail(&smtp, &message)) {
    Serial.println("Erro ao enviar e-mail, " + smtp.errorReason());
    return false;
  }
  return true;
}

/** 
 *  Função de retorno de chamada para obter o status de envio de e-mail 
*/
void smtpCallback(SMTP_Status status) {
  // Imprime o status atual
  Serial.println(status.info());

  // Imprima o resultado do envio
  if (status.success()) {
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Mensagem enviada com sucesso: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Falha na mensagem enviada: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
      /* Obter o item de resultado */
      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 ? "sucesso" : "fracassado");
      ESP_MAIL_PRINTF("Data/Hora: %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("Recebedor: %s\n", result.recipients);
      ESP_MAIL_PRINTF("Sujeito: %s\n", result.subject);
    }
    Serial.println("----------------\n");
  }
}

Explicação do Código

Este algoritmo é semelhante ao Algoritmo de envio do e-mail com corpo de texto simples. Apenas tem divergência na criação da mensagem de texto do corpo do e-mail, que agora é HTML:

  //Define a mensagem de texto à ser enviada
  String messageHTML = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
</head>
<body style="background-color: rgb(250, 241, 192);">
<h1>Teste de HTML!</h1>
<p>Este &eacute; um corpo de texto criado em HTML.</p>
<h3>Exemplos de funcionalidades</h3>
<ol>
<li>
Exemplo de link: <a href="https://google.com.br" target="_blank" rel="noopener">
google.com.br</a>
</li>
<li>Exemplo de Tabela:</li>
</ol>
<table style="border-collapse: collapse; width: 13.0593%; height: 37px;" border="1">
<tbody>
<tr>
<td style="width: 33.3333%;">Coluna 1</td>
<td style="width: 33.3333%;">Coluna 2</td>
</tr>
<tr>
<td style="width: 33.3333%;">1.1</td>
<td style="width: 33.3333%;">1.2</td>
</tr>
</tbody>
</table>
<ul>
<li>
Exemplo de imagem:&nbsp;
<img src=
"https://blog.eletrogate.com/wp-content/themes/blog-eletrogate/dist/img/image/logo.png"
alt="" width="484" height="52" />
</li>
</ul>
<p>&nbsp;</p>
<ul>
<li>
Exemplo de <span style="font-family: 'comic sans ms', sans-serif;">
<span style="background-color: #eccafa;">Formata&ccedil;&atilde;o</span>
&nbsp;de <span style="color: #169179;">Texto</span>
</span>
: <strong>Negrito</strong>,
<em>It&aacute;lico</em>,
<span style="text-decoration: underline;">Sublinhado</span>,
<span style="text-decoration: line-through;">Tachado</span>,
x<sup>2</sup>, H<sub>2</sub>O, <code>CODE;</code>, 😄
</li>
</ul>
<p>&nbsp;</p>
</body>
</html>
)rawliteral"; // "Fim de raw text

Na definição da mensagem HTML do corpo do e-mail, é utilizada a funcionalidade de String bruto da linguagem C++. Uma String de literal bruto é uma string na qual os caracteres de escape como \n , \t ou \” de C++ não são processados. A sintaxe dela é: R “delimiter(raw_characters)delimiter”, em que delimiter pode ter qualquer nome. O conteúdo da string deve estar em raw_characters, podendo ter até quebra de linha.

E também tem divergência na função enviaEmail_HTML(), especificamente na parte de Definição da mensagem:

Ao executar o programa no ESP32, a saída do Monitor Serial é:

22:52:29.606 -> Conectando à rede WiFi
22:52:29.743 -> ................
22:52:32.938 -> WiFi conectado.
22:52:32.938 -> Acquiring time from NTP server...
22:52:32.938 -> 
22:52:33.108 -> Connecting to SMTP server...
22:52:35.147 -> 
22:52:35.147 -> SMTP server connected, wait for greeting...
22:52:35.452 -> 
22:52:35.452 -> Sending greeting response...
22:52:35.792 -> 
22:52:35.792 -> Logging in...
22:52:36.098 -> 
22:52:36.098 -> Sending Email...
22:52:36.098 -> 
22:52:36.098 -> Sending message header...
22:52:36.676 -> 
22:52:36.676 -> Sending message body...
22:52:37.326 -> 
22:52:37.326 -> Finishing the message sending...
22:52:38.041 -> 
22:52:38.041 -> Closing the session...
22:52:38.345 -> 
22:52:38.345 -> Message sent successfully
22:52:38.345 -> 
22:52:38.345 -> ----------------
22:52:38.345 -> Mensagem enviada com sucesso: 1
22:52:38.345 -> Falha na mensagem enviada: 0
22:52:38.345 -> ----------------
22:52:38.345 -> 
22:52:38.345 -> Message No: 1
22:52:38.345 -> Status: sucesso
22:52:38.345 -> Data/Hora: 2022/4/1 22:52:41
22:52:38.345 -> Recebedor: 
22:52:38.345 -> Sujeito: ⸮⸮⸮?
22:52:38.345 -> ----------------
22:52:38.345 -> 

No Gmail do endereço do destinatário, é recebido o e-mail enviado pelo ESP32:

 

Algoritmo de envio de e-mail com anexo de arquivos da SPIFFS

Primeiro, devemos armazenar os arquivos que serão anexos ao e-mail. Para isso, iremos utilizar a SPIFFS (veja mais detalhes sobre no post SPIFFS: Armazenamento de Arquivos do ESP32, aqui mesmo do blog eletrogate).

Para armazenar os arquivos, na IDE Arduino, vá em Sketch → Mostrar a página do sketch.

Na pasta que abrir, crie uma nova pasta chamada data.

Dentro desta pasta coloque os arquivos que você queira armazenar para posterior envio de e-mail com anexo. No nosso caso utilizamos um arquivo de texto chamado arquivo de exemplo.txt e um arquivo de imagem chamado exemplo de imagem.png (ambos podem ser baixados aqui)

Após colocar os arquivos na pasta, novamente na IDE Arduino, vá em Ferramentas → ESP32 Sketch Data upload com o ESP32 conectado na porta USB.

Após a partição SPIFFS estar carregada no ESP32, carregue o seguinte Sketch:

/*******************************************************************************
      Algoritmo de envio de e-mail com anexo de arquivos da SPIFFS com ESP32
                                Sketch de Exemplo

                            Criado em 01 abr. 2022
                               por Michel Galvão

  Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits
                            https://www.eletrogate.com/
*******************************************************************************/

// Inclusão da(s) biblioteca(s)
#include <WiFi.h> // Biblioteca nativa do ESP32
#include <ESP_Mail_Client.h>  // Biblioteca de cliente de e-mail do 
//                                Arduino para enviar.
//                                Referência: https://github.com/mobizt/ESP-Mail-Client

// Configurações da rede WiFi à se conectar
#define WIFI_SSID "<O_SSID_de_sua_rede_WiFi>"
#define WIFI_PASSWORD "<A_senha_de_sua_rede_WiFi>"

// Configurações do SMTP host
#define SMTP_HOST "smtp.gmail.com" // SMTP host
#define SMTP_PORT 465 // SMTP port

// As credenciais de login
#define AUTOR_EMAIL "<O_email_remetente>"
#define AUTOR_SENHA "<A_senha_de_app_do_email_remetente>"

// O objeto de sessão SMTP usado para envio de e-mail
SMTPSession smtp;

// Protótipos das Funções
void smtpCallback(SMTP_Status status);

void setup() {
  // Inicia Serial
  Serial.begin(115200);
  Serial.println();
  delay(1000);

  // Inicia conexão WiFi
  Serial.println("Conectando à rede WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) // Enquanto o status de conexão ao WiFi
    //                                      não for bem sucedido, ...
  {
    Serial.print(".");
    delay(200);
  }

  // Mostra na Serial que a conexão está realoizado e mostra o IP do ESP32 na rede
  Serial.println("\nWiFi conectado.");

  // Habilita a depuração via porta serial:
  //    0: nenhuma depuração
  //    1: deburação básica
  smtp.debug(0);

  // Define a função de retorno de chamada para obter os resultados de envio
  smtp.callback(smtpCallback);

  //Define a mensagem de texto à ser enviada
  String messageTXT = "Teste de envio de E-mail com anexos.";
  // função que envia o e-mail

  // Objeto para declarar os dados de configuração da sessão
  ESP_Mail_Session session;
  // Defina os dados de configuração da sessão
  session.server.host_name = SMTP_HOST;
  session.server.port = SMTP_PORT;
  session.login.email = AUTOR_EMAIL;
  session.login.password = AUTOR_SENHA;
  session.login.user_domain = "";
  // Defina o tempo de configuração do NTP
  session.time.ntp_server = F("time.google.com"); // Utilizado o NTP do Google:
  //                                                  https://developers.google.com/time
  session.time.gmt_offset = -3; // define o deslocamento em segundos
  //                                do fuso horário local em relação ao
  //                                GMT do Meridiano de Greenwich.

  session.time.day_light_offset = 0; // define o deslocamento em segundos do
  //                                      fuso horário local. Este valor costuma
  //                                      ser 3600 para horário de verão +1h ou
  //                                      0 para fusos sem horário de verão.

  // Instanciação do objeto da classe de mensagem
  SMTP_Message message;

  // Definição os cabeçalhos das mensagens
  message.sender.name = "Michel Galvão";
  message.sender.email = AUTOR_EMAIL;
  message.subject = "Teste de E-mail com Anexo";
  message.addRecipient("Michel Galvão", "<O_email_destinatario>");
  message.text.content = messageTXT.c_str();

  // O conjunto de caracteres de mensagem de texto html, por exemplo:
  //  us-ascii
  //  utf-8
  //  utf-7
  // O valor padrão é utf-8
  message.text.charSet = "utf-8";

  // A codificação de transferência de conteúdo. Ex:
  //  enc_7bit ou "7bit" (não codificado)
  //  enc_qp ou "quoted-printable" (codificado)
  //  enc_base64 ou "base64" (codificado)
  //  enc_binary ou "binary" (não codificado)
  //  enc_8bit ou "8bit" (não codificado)
  //  O valor padrão é "7bit"
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  // A prioridade da mensagem:
  //  esp_mail_smtp_priority_high ou 1
  //  esp_mail_smtp_priority_normal ou 3
  //  esp_mail_smtp_priority_low ou 5
  //  O valor padrão é esp_mail_smtp_priority_low
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;

  // As notificações de status de entrega, Ex:
  //  esp_mail_smtp_notify_never
  //  esp_mail_smtp_notify_success
  //  esp_mail_smtp_notify_failure
  //  esp_mail_smtp_notify_delay
  //  O valor padrão é esp_mail_smtp_notify_never
  message.response.notify = esp_mail_smtp_notify_success |
                            esp_mail_smtp_notify_failure |
                            esp_mail_smtp_notify_delay;

  // O item de dados do anexo
  SMTP_Attachment att;
  
  // Defina as informações do anexo, por exemplo: nome do arquivo, tipo MIME, 
  //  caminho do arquivo, tipo de armazenamento do arquivo, codificação de 
  //  transferência e codificação de conteúdo.
  att.descr.filename = "exemplo de imagem.png";
  att.descr.mime = "image/png"; //binary data
  att.file.path = "/exemplo de imagem.png";
  att.file.storage_type = esp_mail_file_storage_type_flash;
  att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;

  // Adiciona anexo à mensagem
  message.addAttachment(att);

  message.resetAttachItem(att); //  limpa os dados internos do item de anexo
  //                                  para estar pronto para reutilização.
  att.descr.filename = "arquivo de exemplo.txt"; // nome do arquivo
  att.descr.mime = "text/plain"; // tipo do arquivo
  att.file.path = "/arquivo de exemplo.txt"; // o caminho do arquivo
  att.file.storage_type = esp_mail_file_storage_type_flash; // O tipo de armazenamento
  att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; //A opção de
  //                                codificar o conteúdo para transferência de dados.

  // Adiciona anexo à mensagem
  message.addAttachment(att);

  // Conecte-se ao servidor com a configuração da sessão
  if (!smtp.connect(&session))
    return;

  // Começa a enviar e-mail e fecha a sessão
  if (!MailClient.sendMail(&smtp, &message)) {
    Serial.println("Erro ao enviar e-mail, " + smtp.errorReason());
  }

  while (1); // loop infinito

}

void loop() {}

/**
    Função de retorno de chamada para obter o status de envio de e-mail
*/
void smtpCallback(SMTP_Status status) {
  // Imprime o status atual
  Serial.println(status.info());

  // Imprima o resultado do envio
  if (status.success()) {
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Mensagem enviada com sucesso: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Falha na mensagem enviada: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
      /* Obter o item de resultado */
      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 ? "sucesso" : "fracassado");
      ESP_MAIL_PRINTF("Data/Hora: %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("Recebedor: %s\n", result.recipients);
      ESP_MAIL_PRINTF("Sujeito: %s\n", result.subject);
    }
    Serial.println("----------------\n");
  }
}

Explicação do Código

Este algoritmo é semelhante aos dois anteriores, tendo apenas a divergência que o algoritmo da função bool enviaEmail_TXT() está dentro do void setup()(no lugar onde era a chamada da função). Para enviar o e-mail junto de anexo, é criado um objeto da classe SMTP_Attachment que armazenará as informações dos anexos. Em seguida, definimos as informações do primeiro anexo (uma imagem): nome do arquivo, tipo do arquivo, caminho do arquivo na SPIFFS, o tipo de armazenamento do arquivo e o tipo codificação de transferência e codificação de conteúdo. Após definirmos as informações do anexo, adicionamos o anexo ao e-mail.

Em seguida limpamos os dados internos do item de anexo para nós o reutilizarmos novamente para outro anexo.

Definimos as informações do segundo anexo (um arquivo de texto): nome do arquivo, tipo do arquivo, caminho do arquivo na SPIFFS, o tipo de armazenamento do arquivo e o tipo codificação de transferência e codificação de conteúdo. Após definirmos as informações do anexo, adicionamos o anexo ao e-mail..

Ao executar o programa no ESP32, a saída do Monitor Serial é:

15:28:07.659 -> Conectando à rede WiFi
15:28:07.799 -> ..................
15:28:11.403 -> WiFi conectado.
15:28:11.403 -> Acquiring time from NTP server...
15:28:11.403 -> 
15:28:11.637 -> Connecting to SMTP server...
15:28:13.697 -> 
15:28:13.697 -> SMTP server connected, wait for greeting...
15:28:14.105 -> 
15:28:14.105 -> Sending greeting response...
15:28:14.406 -> 
15:28:14.406 -> Logging in...
15:28:14.697 -> 
15:28:14.697 -> Sending Email...
15:28:14.697 -> 
15:28:14.697 -> Sending message header...
15:28:15.142 -> 
15:28:15.142 -> Sending message body...
15:28:15.446 -> 
15:28:15.446 -> Sending attachments...
15:28:15.446 -> 
15:28:15.446 -> exemplo de imagem.png
15:28:15.561 -> upload "exemplo de imagem.png", 0%
15:28:15.561 -> upload "exemplo de imagem.png", 5%
15:28:15.561 -> upload "exemplo de imagem.png", 10%
15:28:15.655 -> upload "exemplo de imagem.png", 15%
15:28:15.655 -> upload "exemplo de imagem.png", 20%
15:28:15.655 -> upload "exemplo de imagem.png", 25%
15:28:15.702 -> upload "exemplo de imagem.png", 30%
15:28:15.749 -> upload "exemplo de imagem.png", 35%
15:28:15.782 -> upload "exemplo de imagem.png", 40%
15:28:15.830 -> upload "exemplo de imagem.png", 45%
15:28:15.830 -> upload "exemplo de imagem.png", 50%
15:28:15.830 -> upload "exemplo de imagem.png", 55%
15:28:15.923 -> upload "exemplo de imagem.png", 60%
15:28:15.923 -> upload "exemplo de imagem.png", 65%
15:28:15.970 -> upload "exemplo de imagem.png", 70%
15:28:16.003 -> upload "exemplo de imagem.png", 75%
15:28:16.037 -> upload "exemplo de imagem.png", 80%
15:28:16.079 -> upload "exemplo de imagem.png", 85%
15:28:16.125 -> upload "exemplo de imagem.png", 90%
15:28:16.172 -> upload "exemplo de imagem.png", 95%
15:28:16.206 -> upload "exemplo de imagem.png", 100%
15:28:16.206 -> 
15:28:16.206 -> arquivo de exemplo.txt
15:28:16.308 -> upload "arquivo de exemplo.txt", 0%
15:28:16.308 -> upload "arquivo de exemplo.txt", 7%
15:28:16.308 -> upload "arquivo de exemplo.txt", 15%
15:28:16.308 -> upload "arquivo de exemplo.txt", 23%
15:28:16.308 -> upload "arquivo de exemplo.txt", 31%
15:28:16.308 -> upload "arquivo de exemplo.txt", 39%
15:28:16.308 -> upload "arquivo de exemplo.txt", 47%
15:28:16.308 -> upload "arquivo de exemplo.txt", 55%
15:28:16.308 -> upload "arquivo de exemplo.txt", 63%
15:28:16.308 -> upload "arquivo de exemplo.txt", 71%
15:28:16.308 -> upload "arquivo de exemplo.txt", 78%
15:28:16.308 -> upload "arquivo de exemplo.txt", 86%
15:28:16.308 -> upload "arquivo de exemplo.txt", 94%
15:28:16.308 -> upload "arquivo de exemplo.txt", 100%
15:28:16.308 -> 
15:28:16.308 -> Finishing the message sending...
15:28:17.162 -> 
15:28:17.162 -> Closing the session...
15:28:17.470 -> 
15:28:17.470 -> Message sent successfully
15:28:17.504 -> 
15:28:17.504 -> ----------------
15:28:17.504 -> Mensagem enviada com sucesso: 1
15:28:17.504 -> Falha na mensagem enviada: 0
15:28:17.504 -> ----------------
15:28:17.504 -> 
15:28:17.504 -> Message No: 1
15:28:17.504 -> Status: sucesso
15:28:17.504 -> Data/Hora: 2022/4/4 15:28:13
15:28:17.504 -> Recebedor: ls⸮?
15:28:17.504 -> Sujeito: ⸮⸮⸮?
15:28:17.504 -> ----------------
15:28:17.504 -> 

No Gmail do endereço do destinatário, é recebido o e-mail enviado pelo ESP32:


Hardware do Projeto

Material necessário para o projeto E-mail de Abertura de Porta:

Tendo já estes materiais, monte o circuito de acordo com o seguinte esquemático:

Montagem do alarme na Porta

Na parte superior direita de uma porta, está fixado um suporte de alumínio que é mais largo que o próprio sensor ultrassônico.

O ESP32 e seus sensores estão instalados em uma parede (fixa) na parte superior de uma porta.


Software do Projeto

Bibliotecas Necessárias

Para este projeto, além da biblioteca ESP Mail Client (instruções de instalação descritas anteriormente), é necessário ter a biblioteca HCSR04 ultrasonic sensor.

Para instalá-la na Arduino IDE:

  • vá em Ferramentas → Gerenciar Bibliotecas…

  • Na barra de pesquisa, pesquise hcsr04-ultrasonic-sensor e instale a biblioteca HCSR04 ultrasonic sensor com autor gamegine.

Esta biblioteca permite que uma placa Arduino use um ou mais módulos HCSR04 para obter a distância atual em cm.

Após instalar a biblioteca, carregue o seguinte programa para o ESP32, substituindo:

  • <O_SSID_de_sua_rede_WiFi> para o SSID de sua rede WiFi;
  • <A_senha_de_sua_rede_WiFi> para a senha de sua rede WiFi;
  • <O_email_remetente> para o seu e-mail cadastrado em Senhas de app;
  • <A_senha_de_app_do_email_remetente> para a Senha de app do e-mail;
  • <O_email_destinatario> para o endereço de e-mail ao qual irá enviar o e-mail;
/******************************************************************************
             Alarme de porta com notificação por e-mail com ESP32
                                 Sketch Principal

                             Criado em 31 mar. 2022
                                por Michel Galvão

  Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits
                            https://www.eletrogate.com/
******************************************************************************/

// Inclusão da(s) biblioteca(s)
#include <WiFi.h>
#include <ESP_Mail_Client.h>
#include <HCSR04.h>

#define TRIGGER_PIN  4
#define ECHO_PIN     5

#define PIN_BUZZER 16

// Configurações da rede WiFi à se conectar
#define WIFI_SSID "<O_SSID_de_sua_rede_WiFi>"
#define WIFI_PASSWORD "<A_senha_de_sua_rede_WiFi>"

// Configurações do SMTP host
#define SMTP_HOST "smtp.gmail.com" // SMTP host
#define SMTP_PORT 465 // SMTP port

// As credenciais de login
#define AUTOR_EMAIL "<O_email_remetente>"
#define AUTOR_SENHA "<A_senha_de_app_do_email_remetente>"

// E-mail do destinatário
#define DESTINATARIO_NOME "ADM Alarme"
#define DESTINATARIO_EMAIL "<O_email_destinatario>"

// Objeto para leitura de distância por ultrassom
HCSR04 ultrasonic(TRIGGER_PIN, ECHO_PIN);

// O objeto de sessão SMTP usado para envio de e-mail
SMTPSession smtp;

// Variáveis Globais
const int segundosDeAlarme = 15000; // 15 segundos de alarme tocando, mesmo
//                                      após a porta ter sido fechada.
unsigned long startAlarm;// controle de temporizador de quanto tempo
//                            o alarme deve ficar tocando.
bool alarmeAtivado; // controla se o alarme deve ou não tocar
bool permiteEnvioEmail = false; // controle se deve ou não enviar e-mail. Este
//                                  controle permite que somente um e-mail seja
//                                  enviado a cada abertura da porta, mesmo que o
//                                  tempo de alarme pré-defindo seja ultrapassado.

// Função de retorno de chamada para obter o status de envio de e-mail
void smtpCallback(SMTP_Status status);
bool enviaEmail_TXT(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageTXT,
                    String stmpHost,
                    int stmpPort); // Função de envio de e-mail

void setup() {
  // Inicia Serial
  Serial.begin(115200);
  Serial.println();
  delay(1000);
  pinMode(PIN_BUZZER, OUTPUT);
  digitalWrite(PIN_BUZZER, HIGH);
  delay(100);
  digitalWrite(PIN_BUZZER, LOW);

  // Inicia conexão WiFi
  Serial.println("Conectando à rede WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) // Enquanto o status de conexão ao WiFi
    //                                      não for bem sucedido, ...
  {
    Serial.print(".");
    delay(200);
  }

  // Mostra na Serial que a conexão está realizada
  Serial.println("\nWiFi conectado.");
  Serial.println();
  digitalWrite(PIN_BUZZER, HIGH);
  delay(1000);
  digitalWrite(PIN_BUZZER, LOW);

  // Habilita a depuração via porta serial:
  //    0: nenhuma depuração
  //    1: deburação básica
  smtp.debug(0);

  // Define a função de retorno de chamada para obter os resultados de envio
  smtp.callback(smtpCallback);



}

void loop()
{
  if (alarmeAtivado) { // se o alarme deve tocar, ...
    Serial.println("Alarme executando");

    // Toca o alarme
    digitalWrite(PIN_BUZZER, LOW);
    delay(100);
    digitalWrite(PIN_BUZZER, HIGH);
    delay(200);

    if (millis() - startAlarm >= segundosDeAlarme) // se o tempo do alarme
      //                                                  pré-defindo for
      //                                                  ultrapassado, ...
    {
      alarmeAtivado = false; // não deixa o alarme tocar mais
      digitalWrite(PIN_BUZZER, LOW); // desliga o buzzer
      startAlarm = millis(); // atualiza o tempo do controle de temporizador
    }
  } else // se não (alarme NÃO deve tocar), ...
  {
    float distanciaCm = ultrasonic.dist(); // calcula a distância em centímetros.
    startAlarm = millis(); // atualiza o tempo do controle de temporizador

    Serial.print("distanciaCm: "); Serial.println(distanciaCm);
    if (distanciaCm >= 17) // se a distância for maior ou igual à 17 cm, ...
    {
      alarmeAtivado = true; // deixa o alarme
      if (permiteEnvioEmail == true) // se deve e-mail, ...
      {
        enviaEmail_TXT("ESP32 Alarme de Porta",
                       AUTOR_EMAIL,
                       AUTOR_SENHA,
                       "Alarme da Porta Ativado",
                       DESTINATARIO_NOME,
                       DESTINATARIO_EMAIL,
                       "Foi detectado a abertura da porta",
                       SMTP_HOST,
                       SMTP_PORT); // envia o e-mail com os dados em parâmetros
        permiteEnvioEmail = false; // não deixa enviar e-mail
      }
    } else // se não, ...
    {
      permiteEnvioEmail = true; // deixa enviar e-mail
    }
    delay(100); // pausa de 0,1 segundos
  }

}

bool enviaEmail_TXT(String nomeRemetente,
                    String emailRemetente,
                    String senhaRemetente,
                    String assunto,
                    String nomeDestinatario,
                    String emailDestinatario,
                    String messageTXT,
                    String stmpHost,
                    int stmpPort) {
  // Objeto para declarar os dados de configuração da sessão
  ESP_Mail_Session session;
  // Defina os dados de configuração da sessão
  session.server.host_name = stmpHost;
  session.server.port = stmpPort;
  session.login.email = emailRemetente;
  session.login.password = senhaRemetente;
  session.login.user_domain = "";
  // Defina o tempo de configuração do NTP
  session.time.ntp_server = F("time.google.com"); // Utilizado o NTP do Google:
  //                                         https://developers.google.com/time
  session.time.gmt_offset = -3;
  session.time.day_light_offset = 0;

  // Instanciação do objeto da classe de mensagem
  SMTP_Message message;

  // Definição os cabeçalhos das mensagens
  message.sender.name = nomeRemetente;
  message.sender.email = emailRemetente;
  message.subject = assunto;
  message.addRecipient(nomeDestinatario, emailDestinatario);
  message.text.content = messageTXT.c_str();

  // O conjunto de caracteres de mensagem de texto html, por exemplo:
  //  us-ascii
  //  utf-8
  //  utf-7
  // O valor padrão é utf-8
  message.text.charSet = "utf-8";

  // A codificação de transferência de conteúdo. Ex:
  //  enc_7bit ou "7bit" (não codificado)
  //  enc_qp ou "quoted-printable" (codificado)
  //  enc_base64 ou "base64" (codificado)
  //  enc_binary ou "binary" (não codificado)
  //  enc_8bit ou "8bit" (não codificado)
  //  O valor padrão é "7bit"
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  // A prioridade da mensagem:
  //  esp_mail_smtp_priority_high ou 1
  //  esp_mail_smtp_priority_normal ou 3
  //  esp_mail_smtp_priority_low ou 5
  //  O valor padrão é esp_mail_smtp_priority_low
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;

  // As notificações de status de entrega, Ex:
  //  esp_mail_smtp_notify_never
  //  esp_mail_smtp_notify_success
  //  esp_mail_smtp_notify_failure
  //  esp_mail_smtp_notify_delay
  //  O valor padrão é esp_mail_smtp_notify_never
  message.response.notify = esp_mail_smtp_notify_success |
                            esp_mail_smtp_notify_failure |
                            esp_mail_smtp_notify_delay;

  // Conecte-se ao servidor com a configuração da sessão
  if (!smtp.connect(&session))
    return false;

  // Começa a enviar e-mail e fecha a sessão
  if (!MailClient.sendMail(&smtp, &message)) {
    Serial.println("Erro ao enviar e-mail, " + smtp.errorReason());
    return false;
  }

  return true;
}

/* Função de retorno de chamada para obter o status de envio de e-mail */
void smtpCallback(SMTP_Status status) {
  /* Imprima o status atual */
  Serial.println(status.info());

  /* Imprima o resultado do envio */
  if (status.success()) {
    Serial.println("----------------");
    ESP_MAIL_PRINTF("Mensagem enviada com sucesso: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Falha na mensagem enviada: %d\n", status.failedCount());
    Serial.println("----------------\n");
    struct tm dt;

    for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
      /* Obter o item de resultado */
      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 ? "sucesso" : "fracassado");
      ESP_MAIL_PRINTF("Data/Hora: %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("Recebedor: %s\n", result.recipients);
      ESP_MAIL_PRINTF("Sujeito: %s\n", result.subject);
    }
    Serial.println("----------------\n");
  }
}

Explicação do Código

O código começa incluindo as bibliotecas:

Em seguida, definimos os pinos do Sensor ultrassônico e do buzzer.

Em seguida, definimos o SSID e a senha da rede WiFi.Também definimos o endereço do host e a porta do host SMTP. Em seguida, definimos o endereço e a senha do e-mail ao qual será o remetente. Também definimos o nome e o e-mail do destinatário.

Instanciamos, também, os objetos das classes responsáveis pelo sensor ultrassônico e pelo envio de e-mail.

Declaramos as variáveis globais.

Criamos os protótipos das funções utilizadas no algoritmo.

Em void setup(), iniciamos a Serial.

Iniciamos o pino do buzzer.

E também iniciamos a conexão WiFi.

Após a conexão de WiFi ser bem sucedida, indicamos isso através da Serial e do buzzer.

Ainda em void setup(), desabilitamos a depuração via Serial e definimos a função stmpCallback como callback de resultado de envio.

Em void loop(), verificamos se a variável de controle se o alarme deve ou não tocar está com o valor true. Caso esteja com este valor, tocamos o buzzer para sinalizar que a porta foi aberta. Caso o tempo pré-definido de duração da execução do alarme (definido como 15 segundos nas variáveis globais) seja ultrapassado, desligamos o buzzer, damos o valor false à variável de controle se o alarme deve ou não tocar e atualizamos o valor da variável de temporizador com o valor de millis().

Caso o valor da variável de controle se o alarme deve ou não tocar estiver com o valor false, fazemos a leitura de distância do sensor ultrassônico em centímetros e atualizamos o valor da variável de temporizador com o valor de millis().

Caso a leitura de distância do sensor ultrassônico seja igual ou maior que 17 centímetros, damos o valor true à variável de controle se o alarme deve ou não tocar. Caso a variável de controle se deve ou não enviar e-mail tenha o valor true, fazemos o envio de e-mail de notificação e em seguida damos o valor false à variável de controle se deve ou não enviar e-mail.

Caso a leitura de distância do sensor ultrassônico seja menor que 17 centímetros, permitimos o envio de e-mail.

Por fim, damos um atraso de 100 milissegundos.

Funcionamento teórico

Ao alimentar o ESP32, é tentado à conexão WiFi. Se houver algum reinicio no ESP32 (como por exemplo, ao for detectado Brownout detector was triggered), o buzzer irá ficar sendo acionado a cada 100 milissegundos.

Após a conexão bem-sucedida do ESP32, e a distância detectada for maior que 17 cm (quando a porta for aberta) o ESP32 enviará um único  e-mail de alerta de porta aberta e em seguida disparará o Buzzer.

Enquanto a porta ficar aberta, o alarme ficará acionada mas não enviará novos e-mails. Após fechar a porta, o buzzer soará ainda por 15 segundos. Se uma nova abertura de porta for detectada, um novo e-mail será enviado e o Buzzer será disparado. Este ciclo será repetido sempre.


Funcionamento Final do Projeto

Veja, no vídeo abaixo, o funcionamento prático do projeto:

Antes do acionamento do alarme, no Gmail do destinatário foi recebido o e-mail de alerta:


Conclusão

Com os conceitos aprendidos neste post, é possível criar projetos mais avançados, como o envio de e-mail de alerta, e-mail de status, e-mail contendo informações (como leitura do sensor),  e assim por diante. Ainda, é possível desenvolver projetos para enviar uma foto tirada pelo Módulo ESP32-CAM com Camera OV2640 2MP por e-mail após o acionamento de algum alarme. Curtiu o post? Avalie e deixe um comentário! Siga-nos também no Instagram e nos marque quando fizer algum projeto nosso: @eletrogate. Até a próxima!

Conheça a Metodologia Eletrogate e ofereça aulas de robótica em sua escola!


Sobre o Autor


Michel Galvão

Hobbysta em Sistemas Embarcados e IoT. Tem experiência em Automação Residencial e Agrícola.


Eletrogate

19 de abril de 2022

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!