esp32 intermediate 35 min

ESP32: send email alerts over SMTP, no cloud service

Send email straight from an ESP32 over SMTP with STARTTLS. Works with your own mail server or any provider, no third-party IoT API.

Code available for: ESP32 ArduinoArduino C
Published Sep 22, 2026

Email is the notification channel that never dies. Push services come and go, apps get rewritten, but any email address you own still works in 20 years. An ESP32 can send email directly over SMTP (the protocol every mail server speaks), including through your own mail server or any provider that offers SMTP. No middleman service, no API key from a startup.

This tutorial sends a plain alert email with STARTTLS in about 35 minutes, with a self-hosted angle for each step.

What you need

  • ESP32 dev board with Wi-Fi
  • An SMTP account. Three routes, in the order I would try:
    1. Your own mail server (e.g. the one your domain already runs)
    2. A self-hosted relay on your LAN (MailHog for testing, or a Postfix relay on your Raspberry Pi)
    3. Your existing email provider’s SMTP (e.g. Gmail needs an “app password” now, not your real password)

The SMTP conversation, in plain English

SMTP is a 1980s protocol, all text. Your ESP32 says HELO, the server says hello back, the ESP32 asks to upgrade the connection to TLS (STARTTLS), and only then does the login and mail content flow. The library handles the dance; knowing the shape helps when it breaks.

StepWhoSays
1ESP32Connect TCP port 587
2Server“220 ready”
3ESP32EHLO + STARTTLS
4BothTLS handshake, everything after is encrypted
5ESP32AUTH LOGIN with base64 user/pass
6ESP32MAIL FROM, RCPT TO, DATA
7ESP32The message text, then a line with a single ”.”

Install

Arduino IDE >> Sketch >> Include Library >> Manage Libraries, search “ESP32 MailClient”, install the one by Mobizt (the library is “ESP_Mail_Client”). It handles the TLS handshake and the MIME formatting, which is the part you really do not want to hand-roll.

The code

#include <WiFi.h>
#include <ESP_Mail_Client.h>

#define SMTP_HOST "mail.yourdomain.com"
#define SMTP_PORT 587
#define SMTP_AUTHOR_EMAIL "alerts@yourdomain.com"
#define SMTP_AUTHOR_PASSWORD "your-app-password"
#define TO_EMAIL "you@yourdomain.com"

SMTPSession smtp;
Session_Config config;

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin("your-wifi", "your-password");
  while (WiFi.status() != WL_CONNECTED) delay(500);

  config.server.host_name = SMTP_HOST;
  config.server.port = SMTP_PORT;
  config.login.email = SMTP_AUTHOR_EMAIL;
  config.login.password = SMTP_AUTHOR_PASSWORD;
  config.login.user_domain = "yourdomain.local";

  sendAlert("ESP32 booted", "The plant monitor is online.");
}

bool sendAlert(String subject, String body) {
  config.secure.mode = sec_modes::sec_starttls;   // port 587 way

  SMTP_Message message;
  message.sender.name = "ESP32 Alerts";
  message.sender.email = SMTP_AUTHOR_EMAIL;
  message.subject = subject;
  message.addRecipient("Brian", TO_EMAIL);
  message.text.content = body;
  message.text.charSet = "utf-8";

  if (!smtp.connect(&config)) {
    Serial.print("Connect failed: ");
    Serial.println(smtp.errorReason());
    return false;
  }
  if (!MailClient.sendMail(&smtp, &message)) {
    Serial.print("Send failed: ");
    Serial.println(smtp.errorReason());
    return false;
  }
  Serial.println("Mail sent");
  return true;
}

void loop() {
  // e.g. alert when a sensor crosses a threshold
  delay(60000);
}

The self-hosted test rig

Before you fight a real provider’s TLS rules, test against a fake SMTP server on your LAN. MailHog (single Go binary, runs on a Raspberry Pi in 2 minutes) accepts anything you throw at it and shows it in a web inbox:

# On the Pi:
docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog

Point the ESP32 at 192.168.1.50, port 1025, no TLS, no auth. You get instant feedback on whether your ESP32 code is right, separate from whether your mail provider is having a day. Then flip to the real server by changing four constants.

Port 465 vs 587 is the classic confusion. 587 is submission with STARTTLS (encryption upgrades after plaintext hello). 465 is SMTPS, TLS from byte one. The library’s secure.mode must match: use sec_starttls for 587 and sec_ssl for 465. Wrong pairing gives you a timeout with zero useful information.

App passwords, not your real password

Any major provider now wants an “app password” for non-browser clients (e.g. Google Account >> Security >> 2-Step Verification >> App passwords). That password goes in SMTP_AUTHOR_PASSWORD. It can be revoked independently, which is exactly what you want for a device sitting in your garage.

If you self-host mail: create a dedicated alerts@ account with send-only rights if your server supports it. One device, one account, one revocation switch.

What you learned

  • SMTP with STARTTLS on port 587 is the portable pattern; the library handles the handshake and MIME.
  • A local MailHog relay decouples “is my ESP32 code right” from “is my mail provider right”.
  • App passwords scope the credential to the device.

When something breaks

  • Timeout on connect: you paired port 587 with sec_ssl or port 465 with sec_starttls. The pairing of port and TLS mode is the single most common failure.
  • “Authentication failed”: Gmail and others reject the account password. It wants the app password. Also check the account does not require a browser sign-in first (e.g. Google blocks “less secure” sign-ins from new regions until you have used the app password once from a real client).
  • “530 Must issue a STARTTLS command first”: you are speaking plaintext to a server that demands TLS. That is the library’s secure mode again.
  • Works on USB power, dies on battery: the TLS handshake takes real time and current (e.g. 3-5 seconds at 150 mA). Budget for it in your deep-sleep power math, or move alerts to ntfy (lighter).
  • Mail goes to spam: your domain’s SPF/DKIM do not cover the sending server. That is a DNS problem, not an ESP32 problem. Add the sending server to your SPF record.

What to build next

  • The ntfy tutorial is the lighter-weight notification channel (push instead of email); run both, email for the weekly digest.
  • The MQTT tutorial is the transport for high-frequency sensor data; email is for the 1-in-a-thousand events.
  • The book IoT with ESP32 bundles the connectivity tutorials.