mobizt / ESP-Mail-Client

The comprehensive Arduino Email Client Library to send and read Email for Arduino devices. The library also supports other network shields or modules e.g., Wi-Fi, Ethernet, and GSM/4G modules.
https://mobizt.github.io/ESP-Mail-Client/
MIT License
200 stars 58 forks source link

SMTP plain text sends results in spinlock error #263

Closed PureTek-Innovations closed 1 year ago

PureTek-Innovations commented 1 year ago

Build tool used:

Board used (ESP32/ESP8266/Arudino):

Other Libraries That are used:

When I try and send a plain text email via SMTP using the example https://github.com/mobizt/ESP-Mail-Client/blob/master/examples/SMTP/Send_Text/Send_Text.ino

I get this error WiFi connected IP address: 192.168.1.238

Connecting to SMTP server...

C: ESP Mail Client v3.1.10 C: connecting to SMTP server C: Host > smtpout.secureserver.net C: Port > 465

C: SSL/TLS negotiation C: seeding the random number generator C: setting up the SSL/TLS structure ! W: Skipping SSL Verification. INSECURE! C: setting hostname for TLS session C: perform the SSL/TLS handshake C: verifying peer X.509 certificate

SMTP server connected

C: SMTP server connected, wait for greeting... < S: 220 p3plsmtpa09-06.prod.phx3.secureserver.net :SMTPAUTH: 0KuBqRBaEvGwu : ESMTP server p3plsmtpa09-06.prod.phx3.secureserver.net ready

Sending greeting response...

C: send SMTP command, EHLO < S: 501 EHLO requires valid address C: cleaning SSL connection C: No ESMTP supported, send SMTP command, HELO

Error, connection closed

! E: connection closed

assert failed: spinlock_acquire spinlock.h:122 (result == core_id || result == SPINLOCK_FREE)

Backtrace:0x400836b9:0x3ffb22800x4008df49:0x3ffb22a0 0x400931b9:0x3ffb22c0 0x40090ed7:0x3ffb23f0 0x4008e459:0x3ffb2430 0x400d4db2:0x3ffb2450 0x400e4927:0x3ffb2470 0x400d8caf:0x3ffb2490 0x400dbfc9:0x3ffb24b0 0x400dc592:0x3ffb2550 0x400dfda6:0x3ffb25b0 0x400d384d:0x3ffb25d0 0x400e741e:0x3ffb2820

0 0x400836b9:0x3ffb2280 in panic_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/panic.c:402

1 0x4008df49:0x3ffb22a0 in esp_system_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_system.c:128

2 0x400931b9:0x3ffb22c0 in __assert_func at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/newlib/assert.c:85

3 0x40090ed7:0x3ffb23f0 in spinlock_acquire at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/include/soc/spinlock.h:122

  (inlined by) xPortEnterCriticalTimeout at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/port.c:288

4 0x4008e459:0x3ffb2430 in vPortEnterCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/include/freertos/portmacro.h:578

  (inlined by) xEventGroupClearBits at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/event_groups.c:535

5 0x400d4db2:0x3ffb2450 in WiFiSTAClass::status() at C:/Users/Jem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiSTA.cpp:149

6 0x400e4927:0x3ffb2470 in ESP32_TCP_Client::networkReady() at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/wcs/esp32/ESP32_TCP_Client.cpp:176

7 0x400d8caf:0x3ffb2490 in ESP_Mail_Client::reconnect(SMTPSession*, unsigned long) at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/ESP_Mail_SMTP.h:3006

  (inlined by) ESP_Mail_Client::reconnect(SMTPSession*, unsigned long) at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/ESP_Mail_SMTP.h:2999

8 0x400dbfc9:0x3ffb24b0 in ESP_Mail_Client::handleSMTPResponse(SMTPSession*, esp_mail_smtp_command, esp_mail_smtp_status_code, int) at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/ESP_Mail_SMTP.h:2743

9 0x400dc592:0x3ffb2550 in ESP_Mail_Client::smtpAuth(SMTPSession*, bool&) at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/ESP_Mail_SMTP.h:123

10 0x400dfda6:0x3ffb25b0 in SMTPSession::connect(esp_mail_session_config_t*, bool) at .pio/libdeps/ttgo-lora32-v1/ESP Mail Client/src/ESP_Mail_SMTP.h:3344

11 0x400d384d:0x3ffb25d0 in setup() at src/main.cpp:214

12 0x400e741e:0x3ffb2820 in loopTask(void*) at C:/Users/Jem/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:42

ELF file SHA256: 0000000000000000

Share code snippet to reproduce the issue:

PASTE .cpp / .ino code here
#include <Arduino.h>

#include <ESP_Mail_Client.h>

#include <WiFi.h>

const char* ssid     = "xxxx";
const char* password = "xxxxx";

/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com */
#define SMTP_HOST "smtpout.secureserver.net"

/** The smtp port e.g.
 * 123reg Use default outgoing SMTP port 465 or 587
 * 25  or esp_mail_smtp_port_25
 * 465 or esp_mail_smtp_port_465
 * 587 or esp_mail_smtp_port_587
 */
#define SMTP_PORT esp_mail_smtp_port_465 // port 465 is not available for Outlook.com

/* The log in credentials */
#define AUTHOR_EMAIL "xxxxx@xxxxxx.co.uk"
#define AUTHOR_PASSWORD "xxxxxx"

/* Recipient email address */
#define RECIPIENT_EMAIL "jem@teneyes.co.uk"

/* Declare the global used SMTPSession object for SMTP transport */
SMTPSession smtp;

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

void setupWiFi(){
      Serial.begin(115200);
    delay(10);

    // We start by connecting to a WiFi network

    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
}

void setup(){

  setupWiFi();
    /*  Set the network reconnection option */
  MailClient.networkReconnect(true);
    /** Enable the debug via Serial port
   * 0 for no debugging
   * 1 for basic level debugging
   *
   * Debug port can be changed via ESP_MAIL_DEFAULT_DEBUG_PORT in ESP_Mail_FS.h
   */
  smtp.debug(1);

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

  /* Declare the Session_Config for user defined session credentials */
  Session_Config config;

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

  /** Assign your host name or you public IPv4 or IPv6 only
   * as this is the part of EHLO/HELO command to identify the client system
   * to prevent connection rejection.
   * If host name or public IP is not available, ignore this or
   * use generic host "mydomain.net".
   *
   * Assign any text to this option may cause the connection rejection.
   */
  config.login.user_domain = F("myprofessionalmail.com/");//"mydomain.net");

   /* Set the NTP config time */
  config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
  config.time.gmt_offset = 3;
  config.time.day_light_offset = 0;

  /* The full message sending logs can now save to file */
  /* Since v3.0.4, the sent logs stored in smtp.sendingResult will store only the latest message logs */
  // config.sentLogs.filename = "/path/to/log/file";
  // config.sentLogs.storage_type = esp_mail_file_storage_type_flash;

  /** In ESP32, timezone environment will not keep after wake up boot from sleep.
   * The local time will equal to GMT time.
   *
   * To sync or set time with NTP server with the valid local time after wake up boot,
   * set both gmt and day light offsets to 0 and assign the timezone environment string e.g.

     config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
     config.time.gmt_offset = 0;
     config.time.day_light_offset = 0;
     config.time.timezone_env_string = "JST-9"; // for Tokyo

   * The library will get (sync) the time from NTP server without GMT time offset adjustment
   * and set the timezone environment variable later.
   *
   * This timezone environment string will be stored to flash or SD file named "/tze.txt"
   * which set via config.time.timezone_file.
   *
   * See the timezone environment string list from
   * https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
   *
   */

  /* Declare the message class */
  SMTP_Message message;

  /* Set the message headers */
  message.sender.name = F("TenEyes wind monitoring system");
  message.sender.email = AUTHOR_EMAIL;
  message.subject = F("Test sending plain text Email");
  message.addRecipient(F("Jem"), RECIPIENT_EMAIL);

  String textMsg = "This is simple plain text message";
  message.text.content = textMsg;

  /** If the message to send is a large string, to reduce the memory used from internal copying  while sending,
   * you can assign string to message.text.blob by cast your string to uint8_t array like this
   *
   * String myBigString = "..... ......";
   * message.text.blob.data = (uint8_t *)myBigString.c_str();
   * message.text.blob.size = myBigString.length();
   *
   * or assign string to message.text.nonCopyContent, like this
   *
   * message.text.nonCopyContent = myBigString.c_str();
   *
   * Only base64 encoding is supported for content transfer encoding in this case.
   */

  /** The Plain text message character set e.g.
   * us-ascii
   * utf-8
   * utf-7
   * The default value is utf-8
   */
  message.text.charSet = F("us-ascii");

  /** The content transfer encoding e.g.
   * enc_7bit or "7bit" (not encoded)
   * enc_qp or "quoted-printable" (encoded)
   * enc_base64 or "base64" (encoded)
   * enc_binary or "binary" (not encoded)
   * enc_8bit or "8bit" (not encoded)
   * The default value is "7bit"
   */
  message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;

  // If this is a reply message
  // message.in_reply_to = "<parent message id>";
  // message.references = "<parent references> <parent message id>";

  /** The message priority
   * esp_mail_smtp_priority_high or 1
   * esp_mail_smtp_priority_normal or 3
   * esp_mail_smtp_priority_low or 5
   * The default value is esp_mail_smtp_priority_low
   */
  message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_high;

  message.response.reply_to = "alerts@teneyes.co.uk";
  message.response.return_path = RECIPIENT_EMAIL; //"someone@somemail.com";

  /** The Delivery Status Notifications e.g.
   * esp_mail_smtp_notify_never
   * esp_mail_smtp_notify_success
   * esp_mail_smtp_notify_failure
   * esp_mail_smtp_notify_delay
   * The default value is esp_mail_smtp_notify_never
   */
  // message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;

  /* Set the custom message header */
  message.addHeader(F("Message-ID: <alerts@teneyes.co.uk>"));

  // For Root CA certificate verification (ESP8266 and ESP32 only)
  // config.certificate.cert_data = rootCACert;
  // or
  // config.certificate.cert_file = "/path/to/der/file";
  // config.certificate.cert_file_storage_type = esp_mail_file_storage_type_flash; // esp_mail_file_storage_type_sd
  // config.certificate.verify = true;

  // The WiFiNINA firmware the Root CA certification can be added via the option in Firmware update tool in Arduino IDE

  /* Connect to server with the session config */

  // Library will be trying to sync the time with NTP server if time is never sync or set.
  // This is 10 seconds blocking process.
  // If time synching was timed out, the error "NTP server time synching timed out" will show via debug and callback function.
  // You can manually sync time by yourself with NTP library or calling configTime in ESP32 and ESP8266.
  // Time can be set manually with provided timestamp to function smtp.setSystemTime.

  /* Connect to the server */
  if (!smtp.connect(&config))
  {
    ESP_MAIL_PRINTF("Connection error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
    return;
  }

  /** Or connect without log in and log in later */

    //  if (!smtp.connect(&config, false))
    //    return;

    //  if (!smtp.loginWithPassword(AUTHOR_EMAIL, AUTHOR_PASSWORD))
    //    return;

  if (!smtp.isLoggedIn())
  {
    Serial.println("\nNot yet logged in.");
  }
  else
  {
    if (smtp.isAuthenticated())
      Serial.println("\nSuccessfully logged in.");
    else
      Serial.println("\nConnected with no Auth.");
  }

  /* Start sending Email and close the session */
  if (!MailClient.sendMail(&smtp, &message))
    ESP_MAIL_PRINTF("Error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());

  // to clear sending result log
  // smtp.sendingResult.clear();
}

/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status)
{
  /* Print the current status */
  Serial.println(status.info());

  /* Print the sending result */
  if (status.success())
  {
    // ESP_MAIL_PRINTF used in the examples is for format printing via debug Serial port
    // that works for all supported Arduino platform SDKs e.g. AVR, SAMD, ESP32 and ESP8266.
    // In ESP8266 and ESP32, you can use Serial.printf directly.

    Serial.println("----------------");
    ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
    ESP_MAIL_PRINTF("Message sent failed: %d\n", status.failedCount());
    Serial.println("----------------\n");

    for (size_t i = 0; i < smtp.sendingResult.size(); i++)
    {
      /* Get the result item */
      SMTP_Result result = smtp.sendingResult.getItem(i);

      // In case, ESP32, ESP8266 and SAMD device, the timestamp get from result.timestamp should be valid if
      // your device time was synched with NTP server.
      // Other devices may show invalid timestamp as the device time was not set i.e. it will show Jan 1, 1970.
      // You can call smtp.setSystemTime(xxx) to set device time manually. Where xxx is timestamp (seconds since Jan 1, 1970)

      ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
      ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
      ESP_MAIL_PRINTF("Date/Time: %s\n", MailClient.Time.getDateTimeString(result.timestamp, "%B %d, %Y %H:%M:%S").c_str());
      ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
      ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
    }
    Serial.println("----------------\n");

    // You need to clear sending result as the memory usage will grow up.
    smtp.sendingResult.clear();
  }
}

void loop(){}

Additional information and things you've tried: I've tried different ports. I've checked that I can ping the SMTP server I've tried /* Or connect without log in and log in later /

mobizt commented 1 year ago

This is not library issue, but your information you provided was rejected by server. < S: 501 EHLO requires valid address.

This is not valid host and your mail server was rejected the connection. myprofessionalmail.com

For config.login.user_domain, you can provide your host public IP of your system or public domain name or even try the loopback address "127.0.0.1".

PureTek-Innovations commented 1 year ago

Thank you very much for your help. It is strange that a failed login causes the ESP32 to crash though.

Will the library always do this, if the login fails? I intend to use it for notifications from another system, and if this is standard behavior, I will not be able to use it.

mobizt commented 1 year ago

Yes, I agree for strange assert error, and it is in ESP32 core library WiFi.h when the library is trying to call WiFi.status(). #5 0x400d4db2:0x3ffb2450 in WiFiSTAClass::status() at C:/Users/Jem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiSTA.cpp:149

And that error is outside of the library scope. You should set the debug option for WiFi in Arduino IDE.

PureTek-Innovations commented 1 year ago

The loopback address "127.0.0.1" works, thank you very much.

I've added the ESP32Ping library so that the code only tries to connect and send an email if a ping to the SMTP server is successful.

When my system is in the field, it has a 4G internet connection and these are not 100% reliable.

Hopefully, this will stop it from rebooting if there are any connection issues. It would be good if the spinlock error could be caught in the library though.

mobizt commented 1 year ago

It's not possible to detect the error that does not rise from library but in the core.