Master ESP32 SMTP | Effortlessly Send Emails with Gmail!


ESP32 SMTP Integration with Gmail Email Sending Functionality is one of the Essential Skills in IoT Projects.
This complete tutorial will cover technical details including Wi-Fi connection, TLS encrypted connection to Gmail, SMTP command handshake, Base64 authentication, and more. It’s perfect for developers looking to enhance their ESP32 application capabilities.

Want to know how ESP32 sends Gmail emails via ESP32 SMTP?
This tutorial will walk you step-by-step from connecting to Wi-Fi to successfully sending your first email, thoroughly explaining Gmail SMTP authentication, TLS secure connection, and email sending flow!

ESP32 SMTP

What is SMTP?

SMTP (Simple Mail Transfer Protocol) is a standard Internet protocol used to send emails. Simply put, it’s the “postman” of email, responsible for transferring your mail from the sender to the recipient’s email server.

ESP32 is a Wi-Fi-capable microcontroller. We can use ESP32 with SMTP and Gmail or other mail services to make the ESP32 SMTP send emails automatically.

Development Environment

Before starting your programming, make sure to complete the following preparations:

Wi-Fi Connection

ESP32 SMTP needs to connect to a Wi-Fi network to access the Internet and connect to Gmail’s SMTP server.

// WiFi credentials - replace with your actual network information
#define WIFI_SSID "your_wifi_ssid"
#define WIFI_PASS "your_wifi_password"

// Function to initialize and connect to WiFi in station mode
void wifi_init_sta() {
    // Create default WiFi station network interface
    esp_netif_create_default_wifi_sta();

    // Initialize WiFi with default configuration
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Configure WiFi station settings
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,          // Set SSID (network name)
            .password = WIFI_PASS,       // Set WiFi password
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,  // Minimum security protocol
        },
    };

    // Set WiFi to station mode (client mode)
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    
    // Apply the WiFi configuration
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    
    // Start the WiFi service
    ESP_ERROR_CHECK(esp_wifi_start());
    
    // Initiate connection to the configured WiFi network
    ESP_ERROR_CHECK(esp_wifi_connect());
}

Wi-Fi Event Handling

We need to monitor Wi-Fi status changes, especially to start the email task after obtaining an IP address.

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
        case WIFI_EVENT_STA_START:
            ESP_LOGI(TAG, "WiFi station started");
            break;
        case WIFI_EVENT_STA_CONNECTED:
            ESP_LOGI(TAG, "WiFi connected successfully");
            break;
        case WIFI_EVENT_STA_DISCONNECTED:
            ESP_LOGI(TAG, "WiFi disconnected");
            break;
        }
    }
    else if (event_base == IP_EVENT)
    {
        if (event_id == IP_EVENT_STA_GOT_IP)
        {
            ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
            ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));

            // Create SMTP task once we have an IP address
            xTaskCreate(smtp_task, "smtp_task", SMTP_TASK_STACK_SIZE, NULL, 5, NULL);
        }
    }
}

Establishing ESP32 SMTP Connection

We’ll use Gmail’s SMTP server for sending emails. The server address is smtp.gmail.com, port 465, using SSL/TLS encryption.

We need to use the esp_tls library because ESP32 SMTP must support SSL/TLS.

Base64 Encoding Function

During the ESP32 SMTP authentication process, the email and password need to be Base64 encoded. Here’s a simple base64_encode function for that.

char* base64_encode(const unsigned char *data, size_t input_length) {
    const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    size_t output_length = 4 * ((input_length + 2) / 3);
    char *encoded_data = malloc(output_length + 1);
    
    if (encoded_data == NULL) return NULL;

    for (size_t i = 0, j = 0; i < input_length;) {
        uint32_t octet_a = i < input_length ? data[i++] : 0;
        uint32_t octet_b = i < input_length ? data[i++] : 0;
        uint32_t octet_c = i < input_length ? data[i++] : 0;
        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[j++] = base64_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 0 * 6) & 0x3F];
    }

    for (size_t i = 0; i < (3 - input_length % 3) % 3; i++) {
        encoded_data[output_length - 1 - i] = '=';
    }
    encoded_data[output_length] = '\0';
    return encoded_data;
}

Email Sending Function

In the email sending process, we use SSL/TLS encryption to communicate with Gmail’s SMTP server and send mail via the ESP32 SMTP protocol.

void send_email() {
    ESP_LOGI(TAG, "Starting SMTP connection, free heap: %d bytes", esp_get_free_heap_size());

    // Configure TLS settings for secure SMTP connection
    esp_tls_cfg_t tls_cfg = {
        .timeout_ms = SMTP_TIMEOUT * 1000,  // Connection timeout
        .crt_bundle_attach = esp_crt_bundle_attach,  // Use ESP32's certificate bundle
    };

    // Initialize TLS structure
    esp_tls_t *tls = esp_tls_init();
    if (!tls) {
        ESP_LOGE(TAG, "Failed to initialize TLS");
        return;
    }

    // Establish SSL connection to SMTP server (port 465 for SMTPS)
    if (!esp_tls_conn_new_sync(SMTP_HOST, strlen(SMTP_HOST), SMTP_PORT, &tls_cfg, tls)) {
        ESP_LOGE(TAG, "SSL connection failed");
        esp_tls_conn_delete(tls);
        return;
    }

    // SMTP protocol exchange
    if (!smtp_exchange(tls, NULL, "220")) goto cleanup;  // Wait for server greeting
    if (!smtp_exchange(tls, "EHLO ESP32\r\n", "250")) goto cleanup;  // Send EHLO
    
    // Authentication process
    char *auth_cmd = malloc(128);
    if (!auth_cmd) goto cleanup;
    
    // Initiate LOGIN authentication
    if (!smtp_exchange(tls, "AUTH LOGIN\r\n", "334")) goto free_auth;
    
    // Send base64-encoded username
    char *encoded = base64_encode((const unsigned char *)SENDER_EMAIL, strlen(SENDER_EMAIL));
    if (!encoded) goto free_auth;
    snprintf(auth_cmd, 128, "%s\r\n", encoded);
    free(encoded);
    
    if (!smtp_exchange(tls, auth_cmd, "334")) goto free_auth;
    
    // Send base64-encoded password
    encoded = base64_encode((const unsigned char *)SENDER_PASSWORD, strlen(SENDER_PASSWORD));
    if (!encoded) goto free_auth;
    snprintf(auth_cmd, 128, "%s\r\n", encoded);
    free(encoded);
    
    if (!smtp_exchange(tls, auth_cmd, "235")) goto free_auth;  // Expect authentication success
    
    // Prepare email content
    char *email_data = malloc(256);
    if (!email_data) goto free_auth;
    
    // Set sender
    snprintf(email_data, 256, "MAIL FROM:<%s>\r\n", SENDER_EMAIL);
    if (!smtp_exchange(tls, email_data, "250")) goto free_all;
    
    // Set recipient
    snprintf(email_data, 256, "RCPT TO:<%s>\r\n", RECIPIENT_EMAIL);
    if (!smtp_exchange(tls, email_data, "250")) goto free_all;
    
    // Begin data transmission
    if (!smtp_exchange(tls, "DATA\r\n", "354")) goto free_all;
    
    // Compose email headers and body
    snprintf(email_data, 256,
        "From: %s\r\nTo: %s\r\nSubject: ESP32 Test\r\n\r\n"
        "Hello World!\r\n.\r\n",  // The dot on a line by itself ends the message
        SENDER_EMAIL, RECIPIENT_EMAIL);
    
    // Send the actual email content
    smtp_exchange(tls, email_data, "250");  // Expect 250 OK response
    
free_all:
    free(email_data);
free_auth:
    free(auth_cmd);
cleanup:
    // Gracefully terminate the SMTP session
    smtp_exchange(tls, "QUIT\r\n", NULL);
    esp_tls_conn_delete(tls);
    ESP_LOGI(TAG, "SMTP transaction completed");
}

Google App Password

Due to Google’s security restrictions on using account passwords for third-party apps (especially with 2-Step Verification enabled), you must use a Google App Password for this operation. This password is generated for a specific app/device and replaces your account password for authentication.

Step 1: Enable Google 2-Step Verification

  • Log in to your Google account:
    Visit the Google Account Page, and log in.
  • Enable 2-Step Verification:
    Under “Security,” enable “2-Step Verification.”
    Follow the prompts to complete setup — you’ll need to link your phone or another method.

Step 2: Generate an App Password

  • After enabling 2FA, go to the “Security” section of your Google Account.
    Find the “App Passwords” option.
  • Click “Generate App Password”.
  • Choose “App” > Select “Other (Custom name)” and enter a name like “ESP32 SMTP”.
  • Click Generate. Google will display a 16-character app password (e.g., abcd efgh ijkl mnop).
  • Copy the App Password:
    This is what you’ll use in the ESP32 code instead of your normal password.

Step 3: Use the App Password in Code

Replace the original Google account password in the code with the generated App Password:

#define SENDER_PASSWORD "your_app_password"  // Use generated app-specific password for Gmail

Complete ESP32 SMTP Code

Here is the complete ESP32 SMTP code:

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_tls.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "esp_crt_bundle.h"

// Configuration Section ============================================
#define WIFI_SSID "your_wifi_ssid"        // Wi-Fi SSID
#define WIFI_PASS "your_wifi_password"    // Wi-Fi password
#define SMTP_HOST "smtp.gmail.com"        // Gmail SMTP server address
#define SMTP_PORT 465                     // SMTP port (SSL)
#define SENDER_EMAIL "your_email@gmail.com"  // Sender's Gmail address
#define SENDER_PASSWORD "your_app_password"  // Gmail app password (generated in Google Account)
#define RECIPIENT_EMAIL "recipient_email@gmail.com"  // Recipient's email address
#define SMTP_TIMEOUT 10                   // SMTP timeout in seconds
#define SMTP_TASK_STACK_SIZE 8192        // Stack size for SMTP task
#define MAX_RETRIES 5                    // Maximum retries for sending email
// ===============================================================

static const char *TAG = "SMTP_CLIENT";
static int s_retry_num = 0;

// Base64 encoding function
char* base64_encode(const unsigned char *data, size_t input_length) {
    const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    size_t output_length = 4 * ((input_length + 2) / 3);
    char *encoded_data = malloc(output_length + 1);
    
    if (encoded_data == NULL) return NULL;

    for (size_t i = 0, j = 0; i < input_length;) {
        uint32_t octet_a = i < input_length ? data[i++] : 0;
        uint32_t octet_b = i < input_length ? data[i++] : 0;
        uint32_t octet_c = i < input_length ? data[i++] : 0;
        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[j++] = base64_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[j++] = base64_table[(triple >> 0 * 6) & 0x3F];
    }

    for (size_t i = 0; i < (3 - input_length % 3) % 3; i++) {
        encoded_data[output_length - 1 - i] = '=';
    }
    encoded_data[output_length] = '\0';
    return encoded_data;
}

// Function to handle SMTP communication with the server
bool smtp_exchange(esp_tls_t *tls, const char *send_str, const char *expect_resp) {
    char *buf = malloc(256);  // Allocate buffer for response
    if (!buf) {
        ESP_LOGE(TAG, "Failed to allocate buffer");
        return false;
    }

    if (send_str != NULL) {
        ESP_LOGI(TAG, "C: %s", send_str);  // Log sent command
        if (esp_tls_conn_write(tls, send_str, strlen(send_str)) < 0) {
            free(buf);
            return false;
        }
    }

    int len = esp_tls_conn_read(tls, buf, 255);
    if (len <= 0) {
        free(buf);
        return false;
    }
    buf[len] = '\0';
    ESP_LOGI(TAG, "S: %.128s", buf);  // Log server response (limited to 128 chars)

    bool success = (expect_resp == NULL) || (strstr(buf, expect_resp) != NULL);
    free(buf);
    return success;
}

// Function to send email
void send_email() {
    ESP_LOGI(TAG, "Starting SMTP connection, remaining heap: %d", esp_get_free_heap_size());

    // Establish SSL connection
    esp_tls_cfg_t tls_cfg = {
        .timeout_ms = SMTP_TIMEOUT * 1000,
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    esp_tls_t *tls = esp_tls_init();
    if (!tls) {
        ESP_LOGE(TAG, "Failed to initialize TLS");
        return;
    }

    // Connect using SSL (port 465)
    if (!esp_tls_conn_new_sync(SMTP_HOST, strlen(SMTP_HOST), SMTP_PORT, &tls_cfg, tls)) {
        ESP_LOGE(TAG, "SSL connection failed");
        esp_tls_conn_delete(tls);
        return;
    }

    // SMTP protocol exchange
    if (!smtp_exchange(tls, NULL, "220")) goto cleanup;
    if (!smtp_exchange(tls, "EHLO ESP32\r\n", "250")) goto cleanup;
    
    // Authentication process
    char *auth_cmd = malloc(128);
    if (!auth_cmd) goto cleanup;
    
    if (!smtp_exchange(tls, "AUTH LOGIN\r\n", "334")) goto free_auth;
    
    char *encoded = base64_encode((const unsigned char *)SENDER_EMAIL, strlen(SENDER_EMAIL));
    if (!encoded) goto free_auth;
    snprintf(auth_cmd, 128, "%s\r\n", encoded);
    free(encoded);
    
    if (!smtp_exchange(tls, auth_cmd, "334")) goto free_auth;
    
    encoded = base64_encode((const unsigned char *)SENDER_PASSWORD, strlen(SENDER_PASSWORD));
    if (!encoded) goto free_auth;
    snprintf(auth_cmd, 128, "%s\r\n", encoded);
    free(encoded);
    
    if (!smtp_exchange(tls, auth_cmd, "235")) goto free_auth;
    
    // Email content
    char *email_data = malloc(256);
    if (!email_data) goto free_auth;
    
    snprintf(email_data, 256, "MAIL FROM:<%s>\r\n", SENDER_EMAIL);
    if (!smtp_exchange(tls, email_data, "250")) goto free_all;
    
    snprintf(email_data, 256, "RCPT TO:<%s>\r\n", RECIPIENT_EMAIL);
    if (!smtp_exchange(tls, email_data, "250")) goto free_all;
    
    if (!smtp_exchange(tls, "DATA\r\n", "354")) goto free_all;
    
    snprintf(email_data, 256,
        "From: %s\r\nTo: %s\r\nSubject: ESP32 Test\r\n\r\n"
        "Hello World!\r\n.\r\n",
        SENDER_EMAIL, RECIPIENT_EMAIL);
    
    smtp_exchange(tls, email_data, "250");
    
free_all:
    free(email_data);
free_auth:
    free(auth_cmd);
cleanup:
    smtp_exchange(tls, "QUIT\r\n", NULL);
    esp_tls_conn_delete(tls);
    ESP_LOGI(TAG, "SMTP process completed");
}

// SMTP task function
void smtp_task(void *pvParameters) {
    ESP_LOGI(TAG, "SMTP task started");

    // Send email
    send_email();

    vTaskDelete(NULL);  // Task ends
}

// Wi-Fi event handler function
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
        case WIFI_EVENT_STA_START:
            ESP_LOGI(TAG, "WiFi STA started");
            break;
        case WIFI_EVENT_STA_CONNECTED:
            ESP_LOGI(TAG, "WiFi connected successfully");
            s_retry_num = 0;
            break;
        case WIFI_EVENT_STA_DISCONNECTED:
            if (s_retry_num < MAX_RETRIES)
            {
                ESP_LOGI(TAG, "Attempting to reconnect (%d/%d)", s_retry_num + 1, MAX_RETRIES);
                esp_wifi_connect();
                s_retry_num++;
            }
            else
            {
                ESP_LOGE(TAG, "Connection failed, exceeded maximum retry attempts");
            }
            break;
        }
    }
    else if (event_base == IP_EVENT)
    {
        if (event_id == IP_EVENT_STA_GOT_IP)
        {
            ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
            ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));

            // Create SMTP task
            xTaskCreate(smtp_task, "smtp_task", SMTP_TASK_STACK_SIZE, NULL, 5, NULL);
        }
    }
}P_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));

            // Create SMTP task once IP is acquired
            xTaskCreate(smtp_task, "smtp_task", SMTP_TASK_STACK_SIZE, NULL, 5, NULL);
        }
    }
}

// Wi-Fi initialization
void wifi_init_sta() {
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Register event handler
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_connect());
}

void app_main() {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    wifi_init_sta();

    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

Compilation and Flashing

After completing the code, you can use the commands provided by ESP-IDF to compileflash, and monitor.

Check Your Inbox

After flashing and running the program, check your email inbox to verify that you have received the test email sent by your ESP32 SMTP device.

Conclusion

Through the complete implementation of this project, we have comprehensively mastered the core technologies for integrating ESP32 SMTP email services on the ESP32 platform. This ESP32 SMTP solution fully utilizes the chip’s Wi-Fi and hardware encryption acceleration advantages, establishing an encrypted connection with the ESP32 SMTP server via the TLS security protocol.

During implementation, we specifically optimized memory management for the ESP32 SMTP protocol stack, effectively addressing the challenges of sending large emails in resource-constrained environments through chunked processing techniques, while maintaining full ESP32 SMTP protocol compatibility.