Tuesday, December 8, 2015

Email from Raspberry PI or the Esp8266

UPDATED:
Added Details and Links

If you have been following my recent blog post, I posted about using "curl" to send SMS messages to a Cell Phone Number, I have also created a software function to send a similar SMS messages from Esp8266, and/or using the same strategy this could be used on a Raspberry PI. The SMS messages are transferred to the "textbelt.com" Open SMS Gateway. Access is via a HTML URL post, or via a command line "curl" command which simulates a HTTP post. The textbelt.com server imposes some limits, that is; no more than 3 messages per 3 minutes, and no more than 75 messages per day from a single IPA.

The folks at Textbelt are gracious to allow us "small-time" users to access their gateway for FREE, - SPAMMERS  are not welcome.

But, if you want your Raspberry PI or Esp8266 to send an actual "email", that is another problem. Accessing an "Open" Simple Mail Transport Protocol Gateways (an SMTP) is normally a problem for most users. The reason an "Open" SMTP is desirable for our Internet-of-Things (IoT's) is that authentication is NOT required on Open SMTP's, and therefore there is NO need for a "clear text" password saved in a file, nor is it transferred over the Internet.

There are a few Open SMTP available, where services are purchased by SPAMMERS to do their thing. A few of these SMTP gateways allow low-volume users access to their services for FREE, with some restrictions.  I have tried several Open SMTP gateways, and found most have additional requirements that I did not agree, like; sign up with a credit card.

I am currently using "smtp2go.com" which has a FREE Option and does not require a credit card to sign up. For low volume users like myself, it has a limit of 25 emails per hour, and/or 1000 emails per month, - which is more than enough email for my IoT's to report status, and/or other reports to me via email.

I have implemented a Mail Transfer Agent (MTA) which "talks" to the Open SMTP to send my messages. Messages can be sent to my normal Gmail Account, or to my Cell Phone as an SMS text message, or to any other email address that I desire.

I have written complete MTA function for my use with my ERB-EspWebServer Farm, but provided just some excerpts here for discussion. At some point I will post the complete listing on my GitHub.

Sorry, I did not include the "set up", but I think the upper case "#define" are obvious.



// ###########################################################
//////////////////////////////////////////////////////////////
//
//
boolean
_MTA(WiFiClient client, String aStr, String aPat, unsigned long aTimeout)
{
    
    boolean myStatus = false;
         
    if (aStr.length() ) client.println(aStr);

    yield();

    // Wait For Response
    unsigned long ts = millis();
    while (true) {
        if ( ts + aTimeout < millis() ) {
           myStatus = false;
           break;
        }
        
        yield();
        #if (OPT_MONITOR_DEBUG_OUTPUT) > 0 
            DEBUG_INDENT; Serial.println(sE(" Waiting: ") + aPat + E(" . . . ") );
        #endif
        
        String line = client.readStringUntil('\n');
        
        #if (OPT_MONITOR_DEBUG_OUTPUT) > 0 
            DEBUG_INDENT; Serial.println(sE("  ") + line);
        #endif
        
        if(line.indexOf(aPat) >= 0) {
            myStatus = true;
            break;
        }
    }

    return myStatus;
}

// ###########################################################
//////////////////////////////////////////////////////////////
//
// Provides easy access to page via Email Gateway
//
boolean
eMTA( String aDomain   = MY_DEFAULT_NODE_DOMAIN,
      String aTo       = MY_DEFAULT_EMAIL_TO,
      String aFrom     = MY_DEFAULT_EMAIL_FROM,
      String aMesg     = MY_DEFAULT_EMAIL_MESSAGE,
      String aHost     = MY_DEFAULT_EMAIL_HOST,
         int aPort     = MY_DEFAULT_EMAIL_PORT )
{
    
    digitalWrite ( gBluLED, ON );
    
        #if (OPT_MONITOR_DEBUG_OUTPUT) > 0
            DEBUG_INC_INDENT; Serial.println ( E("Starting Email Client ...") );
        #endif
      
        boolean myStatus = false;
       
        #if (OPT_MONITOR_DEBUG_OUTPUT) > 0
            DEBUG_INDENT; Serial.println( sE(" Connecting to: ") + aHost );
        #endif
      
        // Use WiFiClient class to create TCP connections
        WiFiClient client;
        if (!client.connect(aHost.c_str(), aPort)) {
            #if (OPT_MONITOR_DEBUG_OUTPUT) > 0
                DEBUG_DEC_INDENT; Serial.println( E(" Connection Failed") );
            #endif
            myStatus = false;
        }
        else {
      
          // Wait for Client to Start Sending
            #if (OPT_MONITOR_DEBUG_OUTPUT) > 0 
                DEBUG_INDENT; Serial.println( E(" Waiting for Connection") );
            #endif

            // The MTA Exchange
            while(true) {
    
                if (_MTA(client, "",                          "220 ", 10*SECs ) == false) break;
                if (_MTA(client, "EHLO " + aDomain,           "250 ", 10*SECs ) == false) break;
                if (_MTA(client, "MAIL FROM:" + aFrom + "",   "250 ", 10*SECs ) == false) break;
                if (_MTA(client, "RCPT TO:" + aTo + "",       "250 ", 10*SECs ) == false) break;
                if (_MTA(client, "DATA",                      "354 ", 10*SECs ) == false) break;
                if (_MTA(client, aMesg + "\r\n.\r\n",         "250 ", 10*SECs ) == false) break;
    
                myStatus = true;
                break;

            }
            
            client.flush();
            client.stop();
            
            #if (OPT_MONITOR_DEBUG_OUTPUT) > 0
                if (myStatus == true) { 
                    DEBUG_DEC_INDENT; Serial.println( E(" Connection Closed Successfully") );
                }
                else { 
                    DEBUG_DEC_INDENT; Serial.println( E(" Connection Closed With Error") );
                }
            #endif

        }
         
    digitalWrite ( gBluLED, OFF );
    
    return myStatus;
  
}


As can be seen, the logic of the MTA is really very simple: send a message, and then wait for a response, repeat as necessary, stop on error or finished.

Note: For the Raspberry IP, this can be implemented as a Bash script, in fact the details for the above program were first worked out using a script on a Linux Workstations.  I will post the script later.

If you find this useful - leave a comment.


-- Home Page: https://WA0UWH.blogspot.com

9 comments:

  1. Of course, you realize you can do it much more simply. Every major carrier has an email to text interface. For instance, on Verizon (my carrier) you can send a text message to 123-456-7890 by sending a simple email to 1234567890@vtext.com. No limits on the number of messages in a time frame, and no need to use curl. Any email client can do it.

    ReplyDelete
  2. Jerry, thanks for the comment

    You are correct, but most Raspberry PI's, Esp8266's, and most Internet-of-Things (IoT) are not configured with "any email client", nor are they configured with a simple SMTP MTA.

    How would you suggest someone send an email from a Raspberry PI, an Esp8266, or IoT, via an automatic process. For example, a simple "Boot" message, or "Temperature Report" to be sent to my phone at 1234567890@vtext.com .

    A Mail-Transfer-Agent (MTA) is needed to make the initial connection to a email delivery system, which is what I have implemented above.

    Maybe, I have missed some configuration that I should consider? :-)

    Regards,
    Eldon - WA0UWH

    ReplyDelete
  3. No, you do not need an MTA to send emails. All you need is an email client such as Outlook or Thunderbird which can connect to an MTA (typically your ISP's, although it can be any MTA you are authorized to use).
    All you need is to connect to the email server, and with simple text commands (using the SMTP protocol - google for the commands), send the email. Simple emails can even be sent from a bash script - it's only text commands/responses.
    The other advantage to just sending it as an email is the SMTP protocol is well defined in RFC's, and will not change (except possibly adding to it). A web page can change at any time, which can cause your cURL script to fail.

    ReplyDelete
  4. I didn't say you should install "Outlook or Thunderbird". I just pointed out that these are email clients which work just fine on a machine without an MTA.
    But since you only show a small part of your code, it's hard telling just what you are doing. I'm just going by your original comment about using cURL (not "curl") and textbelt.com. Neither is necessary to send text messages to a cell phone.

    ReplyDelete
  5. Jerry,

    How would you suggest to install "Outlook or Thunderbird" as an email agent on an Esp8266 (or other IoT) which has less than 32K of RAM (and most of which is Application Code)?

    The code presented in the post above takes the place of the email agent, and it implements the "it's only text commands/responses" as you suggested.

    See Exp8266 at: http://tinyurl.com/o5holfr

    Regards,
    Eldon - WA0UWH

    ReplyDelete
    Replies
    1. Eldon, Outlook and Thunderbird are not MTA's. They are MTA clients, which is what you need. These are just two possibilities. Every ISP I've ever used has it's own MTA which you can easily access with the appropriate commands.
      I think you are very confused as to what a MTA is vs. a mail client. An MTA client is not an email agent.
      Over the last 30+ years I've implemented a number of email clients. All of them have connected to an MTA. And many of them have also sent SMS texts.

      Delete
    2. Jerry, Eldon made a simple nomenclature error. He means MUA (mail user agent). Aside from that, he has implemented exactly what you suggest.
      A "MTA client" is more properly known as MUA. You can see from the article below that every piece of software involved in email handling has "agent" in its designation. So, it actually is an agent.

      https://en.wikipedia.org/wiki/Message_transfer_agent

      Delete
  6. This is great, thanks for sharing. I added subject with this:


    if (_MTA(client, "Subject:" + aSubject + "\r\n\r\n" + aMesg + "\r\n.\r\n", "250 ", 10*SECs ) == false) break;

    ReplyDelete