Sending email with outbound.mailhop.org SMPT server [SOLVED]

This is my first time trying to programmatically send an email.
I found examples of sending emails, but they all use "Sender Email".
outbound.mailhop.org doesn't use an email to authenticate, but rather a username.
I have another device that sends an email using this SMTP server (have no idea what method it uses), these are its settings:

Email Client Protocol:  SMTP - Authenticated
Sender mail address :  Seems like it can be anything, I have it set to a non-existant email address
Send Mail Server (SMTP):  outbound.mailhop.org
Port No. (SMTP):  25
Account:  username
Password:  password

Here is my attempt at massaging Mobizt's ReadyMail library "Attachment.ino"

This is a new library, but his ESP_Email_Client looks similar in the way the SMTP server is set up. I'll use that one, if somebody tells me it's easier or at least something they can help me get running.
Or any other way. I just want to send a .txt file.

/**
 * The example to send message with attachment.
 */
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>

#define ENABLE_SMTP  // Allow SMTP class and data
#define ENABLE_DEBUG // Allow debugging
#define READYMAIL_DEBUG_PORT Serial
#define ENABLE_FS // Allow filesystem integration
#include "ReadyMail.h"

#define SMTP_HOST "outbound.mailhop.org"
#define SMTP_PORT 25
#define DOMAIN_OR_IP ""
#define AUTHOR_EMAIL "?????"  // username and password required, not email
#define AUTHOR_PASSWORD "password"  // password for outbound.mailhop.org account
#define RECIPIENT_EMAIL "my@email.com"

#define WIFI_SSID "WiFi SSID"
#define WIFI_PASSWORD "WiFi Password"

WiFiClientSecure ssl_client;
SMTPClient smtp(ssl_client);

#if defined(ENABLE_FS)
#include <FS.h>
File myFile;
#if defined(ESP32)
#include <SPIFFS.h>
#endif
#define MY_FS SPIFFS

const char *orangeImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAEASURBVHhe7dEhAQAgEMBA2hCT6I+nABMnzsxuzdlDx3oDfxkSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0iMITGGxBgSY0jMBYxyLEpP9PqyAAAAAElFTkSuQmCCjhSDb5FKG9Q4";
const char *blueImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAEASURBVHhe7dEhAQAgAMAwmmEJTyfwFOBiYub2Y6596Bhv4C9DYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJMSTGkBhDYgyJuZ7+qGdQMlUbAAAAAElFTkSuQmCC";

void fileCb(File &file, const char *filename, readymail_file_operating_mode mode)
{
    switch (mode)
    {
    case readymail_file_mode_open_read:
        myFile = MY_FS.open(filename, FILE_OPEN_MODE_READ);
        break;
    case readymail_file_mode_open_write:
        myFile = MY_FS.open(filename, FILE_OPEN_MODE_WRITE);
        break;
    case readymail_file_mode_open_append:
        myFile = MY_FS.open(filename, FILE_OPEN_MODE_APPEND);
        break;
    case readymail_file_mode_remove:
        MY_FS.remove(filename);
        break;
    default:
        break;
    }
    file = myFile;
}

void createAttachment()
{
    MY_FS.begin(true);

    File file = MY_FS.open("/orange.png", FILE_WRITE);
    file.print(orangeImg);
    file.close();

    file = MY_FS.open("/blue.png", FILE_WRITE);
    file.print(blueImg);
    file.close();
}
#endif

void smtpCb(SMTPStatus status)
{
    if (status.progressUpdated)
        ReadyMail.printf("ReadyMail[smtp][%d] Uploading file %s, %d %% completed\n", status.state, status.filename.c_str(), status.progress);
    else
        ReadyMail.printf("ReadyMail[smtp][%d]%s\n", status.state, status.text.c_str());
    // The status.state is the smtp_state enum defined in src/smtp/Common.h
}

void addFileAttachment(SMTPMessage &msg, const String &filename, const String &mime, const String &name, FileCallback cb, const String &filepath, const String &encoding = "", const String &cid = "")
{
    Attachment attachment;
    attachment.filename = filename;
    attachment.mime = mime;
    attachment.name = name;
    // The inline content disposition.
    // Should be matched the image src's cid in html body
    attachment.content_id = cid;
    attachment.attach_file.callback = cb;
    attachment.attach_file.path = filepath;
    // Specify only when content is already encoded.
    attachment.content_encoding = encoding;
    if (cid.length() > 0)
        msg.addInlineImage(attachment);
    else
        msg.addAttachment(attachment);
}

void setup()
{
    Serial.begin(115200);
    Serial.println();

    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("Connecting to Wi-Fi");
    while (WiFi.status() != WL_CONNECTED)
    {
        Serial.print(".");
        delay(300);
    }
    Serial.println();
    Serial.print("Connected with IP: ");
    Serial.println(WiFi.localIP());
    Serial.println();

    createAttachment();

    ssl_client.setInsecure();

    Serial.print(smtp.connect(SMTP_HOST, SMTP_PORT, DOMAIN_OR_IP, smtpCb, false));
    if (!smtp.isConnected())
        return;

    smtp.authenticate(AUTHOR_EMAIL, AUTHOR_PASSWORD, readymail_auth_password);
    if (!smtp.isAuthenticated())
        return;

    SMTPMessage msg;
    msg.sender.name = "ReadyMail";
    msg.sender.email = AUTHOR_EMAIL;
    msg.subject = "ReadyMail Hello message with attachment";
    msg.addRecipient("User", RECIPIENT_EMAIL);

    String bodyText = "Hello everyone.\n";
    msg.text.content = bodyText;
    msg.text.transfer_encoding = "base64";
    msg.html.content = "<html><body><div style=\"color:#00ffff;\">" + bodyText + "</div></body></html>";
    msg.html.transfer_encoding = "base64";

    addFileAttachment(msg, "orange.png", "image/png", "orange.png", fileCb, "/orange.png", "base64");
    addFileAttachment(msg, "blue.png", "image/png", "blue.png", fileCb, "/blue.png", "base64");

    smtp.send(msg);
}

void loop()
{
}

Results:

Connecting to Wi-Fi......
Connected with IP: 192.168.xxx.xxx

ReadyMail[smtp][1] Connecting to outbound.mailhop.org...
0

Apparently, you can't use Port 25 with SSL. Any idea how to not use SSL with this code?
Any idea how to use account username and password?

I tried reading what documentation there is, but it seems like a language / translate barrier, plus my lack of understanding the basics.

mobizt has given me a bit of help.
On his recommendation, or at least what I understood him to recommend, I made the following changes:

included Ethernet.h
Changed:

WifIClientSecure ssl_client;
SMTPClient smtp(ssl_client);

to

EthernetClient basic_client;
SMTPClient smtp(basic_client);

However, I am still getting the same results.

Ok, I don't think my problem at this point is username/email address. I'm not even connecting to outbound.mailhop.org yet.

This may sound strange, but I think that sort of stuff is well documented in Google for adding other mail clients or setting up something. Sorry, that's all I got on that issue, but I would be very surprised if at least one of the Libraries doesn't have all the info.

Thanks. I'll keep digging.

  • This might help, I have never done it though.

That's for wired Ethernet. For a non-SSL/TLS connection over WiFi, use WiFiClient

Yeah, thanks. I realized that and changed it, but it didn't help.
Going to do more digging today.

Thank you. I read this before, but I'll give it a re-read.

So after a few days of pulling my hair out, the issue turned out to be permissions on the network.
I am connected to a dedicated IoT WiFi network at work as I'm working on this project. The network was supposed to be set up and ready to use. Talking with our IT guy, it turns out there were about 4 different things he had to change to enable me to get out to the internet.

After that, emails were being sent.

ReadyMail went from v0.0.5 to v.0.1.3 in the last two weeks, but all of my problems were due to local networking issues. And I did learn some new things concerning SMTP.

Thanks to Mobizt for his patience.


```cpp
#include <Arduino.h>

#include <WiFi.h>
#include <WiFiClientSecure.h>

#include "time.h"
#include "esp_sntp.h"

#include <FS.h>
#include <SD.h>
#include <SPI.h>

#include <Adafruit_MAX31856.h>

/**********  Settings for WiFi **********/
#define WIFI_SSID "MySSID"
#define WIFI_PASSWORD "MyPassword"

/**********  Settings for EMAIL  **********/
#define ENABLE_SMTP   // Allow SMTP class and data
#define ENABLE_DEBUG  // Allow debugging
#define READYMAIL_DEBUG_PORT Serial
#define ENABLE_FS  // Allow filesystem integration
#include "ReadyMail.h"

#define SMTP_HOST "outbound.mailhop.org"
#define SMTP_PORT 465
#define DOMAIN_OR_IP ""
#define AUTHOR_EMAIL "Author Email"
#define AUTHOR_PASSWORD "Author Password"
#define RECIPIENT_EMAIL "Recipient Email"
WiFiClientSecure ssl_client;
SMTPClient smtp(ssl_client);
bool email_sent = false;

/**********  Settings for NTP  **********/
#define NTP_SERVER "pool.ntp.org"
const long GMT_OFFSET_SEC = -18000;
const int DST_OFFSET_SEC = 3600;
struct tm timeinfo;
char time_string[9];
char date_string[15];
char filename_string[20];

const unsigned long read_delay = 60000UL;  // 60 seconds between temperature readings
const unsigned long update_clock_delay = 3600000UL;  // update RTC once per hour
unsigned long temp_reading_previous_millis;
unsigned long clock_sync_previous_millis;

void LocalTime() {
  if(!getLocalTime(&timeinfo))
  {
    // send email to Controls Engineer
    return;
  }
  // separate timeinfo fields into time and date
  snprintf(time_string, 10, "%2d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
  const char *MONTHS[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // lookup for month
  snprintf(date_string, 15, "%s %2d, %d", MONTHS[timeinfo.tm_mon], timeinfo.tm_mday, 1900 + timeinfo.tm_year);
}

void timeavailable(struct timeval *t) {
  LocalTime();
}

/**********  Settings for THERMOCOUPLE  **********/
#define DRDY_PIN 15
Adafruit_MAX31856 thermo = Adafruit_MAX31856(2);

/**********  Settings for SD card  *********/
void fileCb(File &file, const char *filename, readymail_file_operating_mode mode)
{
  file = SD.open(filename, FILE_OPEN_MODE_READ);
}

void newFile()
{
  LocalTime();
  snprintf(filename_string, 20, "/%s.txt", date_string); // add "/" and ".txt" to date_string for full file name
  File file = SD.open(filename_string, FILE_WRITE);
  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message)
{
  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    // send email to admin notifying failure to open the file for appending
    return;
  }
  if (!file.println(message))
  {
    //send email to admin notifying failure write new data to file
  }
  file.close();
}

void readFile(fs::FS &fs, const char *path)
{
  File file = fs.open(path);
  if (!file)
  {
    // send email to admin notifying failure to open file for reading
    return;
  }

  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
}

void deleteFile(fs::FS &fs, const char *path)
{
  if (!fs.remove(path))
  {
    // send email
  }
}

void smtpCb(SMTPStatus status) {
  if (status.progressUpdated)
    ReadyMail.printf("ReadyMail[smtp][%d] Uploading file %s, %d %% completed\n", status.state, status.filename.c_str(), status.progress);
  else
    ReadyMail.printf("ReadyMail[smtp][%d]%s\n", status.state, status.text.c_str());
  // The status.state is the smtp_state enum defined in src/smtp/Common.h
}

void addFileAttachment(SMTPMessage &msg, const String &filename, const String &mime, const String &name, FileCallback cb, const String &filepath, const String &encoding = "", const String &cid = "") {
  Attachment attachment;
  attachment.filename = filename;
  attachment.mime = mime;
  attachment.name = name;
  // The inline content disposition.
  // Should be matched the image src's cid in html body
  attachment.content_id = cid;
  attachment.attach_file.callback = cb;
  attachment.attach_file.path = filepath;
  // Specify only when content is already encoded.
  attachment.content_encoding = encoding;
  msg.attachments.add(attachment, attach_type_attachment);
}

void sendEmail()
{
  char temp[25];
  ssl_client.setInsecure();
  smtp.connect(SMTP_HOST, SMTP_PORT, DOMAIN_OR_IP, smtpCb);
  if (!smtp.isConnected())
  {
    Serial.println("Not connected");
    return;
  }
  smtp.authenticate(AUTHOR_EMAIL, AUTHOR_PASSWORD, readymail_auth_password);
  if (!smtp.isAuthenticated())
  {
    Serial.println("Not authenticated");
    return;
  }
  SMTPMessage msg;
  msg.headers.add(rfc822_from, "Author <" + String(AUTHOR_EMAIL) + ">");
  msg.headers.add(rfc822_subject, "Purpose of this email");
  msg.headers.add(rfc822_to, "Who to <" + String(RECIPIENT_EMAIL) + ">");
  String bodyText = "What's in here?\r\n";
  msg.text.body(bodyText);
  msg.timestamp = time(NULL);
  snprintf(temp, 25, "%s.txt", date_string);
  addFileAttachment(msg, temp, "text/plain", temp, fileCb, filename_string, "");
  smtp.send(msg);
  email_sent = true;
}

void setClock()
{
  sntp_set_time_sync_notification_cb(timeavailable);
  configTime(GMT_OFFSET_SEC, DST_OFFSET_SEC, NTP_SERVER);
}

void setup() {
  Serial.begin(115200);
  if(!SD.begin(5))
  {
    Serial.println("SD Card not detected");
    while(1);
  }
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  thermo.begin();
  thermo.setThermocoupleType(MAX31856_TCTYPE_K);
  pinMode(DRDY_PIN, INPUT);  // not used yet, but ready if we want it
  thermo.setConversionMode(MAX31856_CONTINUOUS);

  esp_sntp_servermode_dhcp(1);

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

  setClock();

  temp_reading_previous_millis = millis();
  clock_sync_previous_millis = millis();

  LocalTime();
  snprintf(filename_string, 20, "/%s.txt", date_string);
  if(!SD.exists(filename_string))
  {
    Serial.println("Creating new file on startup");
    newFile();
  }
  else
  {
    Serial.print("Using existing file: ");
    Serial.println(filename_string);
  }
}


void loop() {
  unsigned long current_millis = millis();
  LocalTime();
  float temperatureC = 0.0;
  int temperatureF = 0;

  if (current_millis - temp_reading_previous_millis >= read_delay)
  {
    char thermo_temp[20]; // create a string to hold time and temperature to be written to file
    temperatureC = thermo.readThermocoupleTemperature();
    temperatureF = (int)(temperatureC * 1.8 + 32);
    snprintf(thermo_temp, 20, "%s %dF°", time_string, temperatureF);
    appendFile(SD, filename_string, thermo_temp);
    temp_reading_previous_millis = current_millis;
  }

  if (current_millis - clock_sync_previous_millis >= update_clock_delay)  // maybe not needed so often, but why not?
  {
    setClock();
    clock_sync_previous_millis = current_millis;
  }

  if(timeinfo.tm_hour == 23 && timeinfo.tm_min == 59 && !email_sent)  // send email at the end of the day
  {
    sendEmail();
    deleteFile(SD, filename_string);  // delete yesterday's file
    newFile();
  }

  if(email_sent && timeinfo.tm_hour == 0 && timeinfo.tm_min == 0 && email_sent)
  {
    email_sent = false;
  }

}