0

I want to get emails read from a Hotmail account using an Arduino Uno. Is there a way to do it with SSL or is there a way to disable SSL?

#include <SPI.h>
#include <Ethernet.h>
// Use comments to enable or disable this define for debug messages
#define DEBUG_POP
// Use comments to enable or disable the deleting of the mail
#define ENABLE_DELETE_POP


// The mac address must be an unique number
// A mac generator is used:
//   http://www.miniwebtool.com/mac-address-generator/
byte mac[] = { 0xDE, 0x36, 0x5F, 0x0A, 0x19, 0x83 };
// change network settings to yours
IPAddress ip(192, 168, 1, 117);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);


// Set the server POP3 address, the port, the user and password.
// The POP3 mail server is something like this: 
//     mail.yourdomain.com, pop.yourdomain.com, pop3.yourdomain.com
// Using PROGMEM for these causes a fail when trying to connect and log in.
const char pop_server[] = "pop3.live.com";
const int  pop_port = 995;
const char pop_user[] = "xxxxxx@hotmail.com";
const char pop_pass[] = "xxxxxx";


// The number of milliseconds timeout for parseInt() and find().
// The response time for the Server can still be 10 seconds.
#define POP_TIMEOUT 10
#define POP_TIMEOUT_DEFAULT 1000


EthernetClient client;


void setup()
{
    Serial.begin(9600);
    Serial.println(F("\nArduino POP3 email reader"));

    pinMode(13, OUTPUT);     // the system led is used for testing

    // When the Ethernet Shield is used, there is also a SD card connected
    // to the SPI bus. Disable the SD card with chip select at pin 4.
    pinMode(4, OUTPUT);
    digitalWrite(4, HIGH);


    // Start Ethernet. Use only the 'mac' parameter for DHCP
    // Use 'mac' and 'ip' parameters for static IP address.
    //  Ethernet.begin( mac, ip);
    //  Ethernet.begin( mac, ip, gateway, gateway, subnet);
    if (Ethernet.begin(mac) == 0)
    {
        Serial.println("Failed to configure Ethernet using DHCP.");
        // no point in carrying on, so do nothing forevermore:
        while (1);
    }
    Ethernet.begin(mac, ip, gateway, gateway, subnet);
    // print your local IP address.
    Serial.println(F("Ethernet started."));
    Serial.print(F("Local IP = "));
    Serial.println(Ethernet.localIP());

    Serial.println(F("Press 'c' to check mail."));
}


void loop()
{
    // Create a buffer to receive the commands in (that is the Subject of the mail).
    char buffer[32];

    byte inChar = Serial.read();
    if (inChar == 'c')
    {
        // The getEmail gets the text of the mail Subject into the buffer.
        // The valid number of received characters are returned.
        // If the return value is < 0, it is an error.
        int n = getEmail(buffer, sizeof(buffer));

        if (n < 0)
        {
            Serial.print(F("Email POP3 failed, error = "));
            Serial.println(n);
        }
        else
        {
            if (n == 0)
            {
                Serial.println(F("Ready, nothing to do."));
            }
            else
            {
                // 'n' is > 0, a command received.
                Serial.print(F("Email checked, Command = \""));
                Serial.print(buffer);
                Serial.println(F("\""));


                // Check the commands.
                //
                // At this moment, a single command 'L' is used to set system led on or off.
                //    L=1   (set led on)
                //    L=0   (set led off)
                if (buffer[0] == 'L' && buffer[1] == '=')
                {
                    digitalWrite(13, buffer[2] == '0' ? LOW : HIGH);
                }
            }
        }
    }
}


// getEmail
// --------
// Find an email on a mail server, using POP3.
// The Subject should start with "ARDUINO " and the text
// after that is copied into pBuf.
// 
// The data in pBuf is only valid if the return value is not an error
// (an error is return value less than zero).
//
int getEmail(char *pBuf, int nBufSize)
{
    // nBytes is the number of bytes that is returned by getEmail.
    int nBytes = 0;

    // Connect to server
    // client.connect returns '1' if okay, or negative number if error.
    //    SUCCESS 1
    //    0     (error, unknown timeout, perhaps an error in the library)
    //    TIMED_OUT -1
    //    INVALID_SERVER -2
    //    TRUNCATED -3
    //    INVALID_RESPONSE -4
    //    -5    (there is no mail server at that IP address and port)
    // The string for the server must be a normal string in sram, no PROGMEM allowed.
    int nError = client.connect(pop_server, pop_port);

    // During testing, a value of zero was sometimes returned.
    // This is not according to the documentation and it is an error.
    // Therefor the non-error value '0' is turned into a negative number to
    // indicate an error.
    if (nError == 0)
        return(-200);

    // Only a value of 1 is okay.
    if (nError != 1)
        return(nError);

#ifdef DEBUG_POP
    Serial.println(F("connected"));
    //Serial.println(nError);
#endif
    // The server should respond with "+OK" and maybe more text after that.
    // Check if "+OK" can be read.
    // The parameter 'true' is to read also everything after the "+OK".
    if (!readOk(true))
        return -102;

#ifdef DEBUG_POP
    Serial.println(F("command USER"));
#endif
    client.print(F("USER "));
    client.println(pop_user);
    if (!readOk(true))
        return -103;

#ifdef DEBUG_POP
    Serial.println(F("command PASS"));
#endif
    client.print(F("PASS "));
    client.println(pop_pass);
    if (!readOk(true))
        return -104;

#ifdef DEBUG_POP
    Serial.println(F("command STAT"));
#endif  
    client.println(F("STAT"));
    if (!readOk(false))
        return -105;

    // The whole line was like this: "+OK 3 15343"
    // It means that 3 emails are waiting with a total size of 15343.
    // At this moment, the "+OK" is read, but nothing else.
    // Check if there is a space after "+OK".
    char c = client.read();
    if (c != ' ')
        return -106;

    client.setTimeout(POP_TIMEOUT);       // set timeout lower for parseInt
    // Read the number of emails that are on the server.
    int nMails = client.parseInt();
    client.setTimeout(POP_TIMEOUT_DEFAULT);  // restore timeout to 1 second

    // Read the remaining of the response to STAT.
    readRemaining();

#ifdef DEBUG_POP
    Serial.print(F("Number of emails="));
    Serial.println(nMails);
#endif

    // Test if there are emails waiting.
    if (nMails == 0)
    {
        // No emails, but no error. Set buffer to empty string.
        nBytes = 0;               // the returned value
        pBuf[0] = '\0';           // set empty string
    }
    else if (nMails > 0)
    {
        // emails are waiting.
        // Scan the emails until the first is found with the keyword "ARDUINO " at the
        // beginning of the "Subject: ".

        boolean found_and_ready = false;
        for (int nMailNumber = 1; nMailNumber <= nMails && !found_and_ready; nMailNumber++)
        {
            // The command RETR <x> gets the whole mail.
            // The command TOP <x> <size> gets the header plus 'size' of the body.

#ifdef DEBUG_POP
            Serial.print(F("command TOP "));
            Serial.print(nMailNumber);
            Serial.println(F(" 0"));
#endif    
            client.print(F("TOP "));
            client.print(nMailNumber);
            client.println(F(" 0"));

            // Use readOk with parameter 'false' to stop reading after "+OK".
            if (!readOk(false))
                return -107;
            // The header of the email is waiting to be read, use the Stream.find() to look for the Subject.
            // The text "Subject: " should be at the beginning of a line, but that is not tested.
            // The first found text "Subject: " is assumed to be the real subject.
            // I have checked many years of emails, and the text is always "Subject: ", and never "SUBJECT: ".
            // At the moment, it is not possible to use the F() macro for Stream.find
            // Only the email that starts with "ARDUINO " at the start of the Subject is used.

            client.setTimeout(POP_TIMEOUT);      // set short timeout for find().
            // find() returns true if found and false if not.
            boolean foundsubject = client.find("Subject: ARDUINO ");
            client.setTimeout(POP_TIMEOUT_DEFAULT);   // restore timeout to 1 second

            if (foundsubject)
            {
#ifdef DEBUG_POP
                Serial.println(F("Found an email for me"));
#endif        

                // Read the remaining subject (that is the command for the Arduino) into a buffer.
                // Every line from the mail server should end with CR + LF, 
                // but to be sure, both CR and LF are checked.
                // Alternative:
                //    client.readBytesUntil('\r', pBuf, nBufSize);

                // The last position in the buffer is reserved for the zero terminator.
                // So read data until (nBufSize-1).
                int i;
                for (i = 0; i < (nBufSize - 1) && client.available(); i++)
                {
                    char c = client.read();
                    if (c == '\r' || c == '\n')
                        break;
                    pBuf[i] = c;
                }
                // Add zero terminator
                pBuf[i] = '\0';
                nBytes = i;     // the number of received bytes is returned by the getEmail() function.

                // More text of the header could be following the Subject.
                // That is read and disregarded.
                readRemaining();

#ifdef DEBUG_POP
                Serial.print(F("Subject = \"ARDUINO "));
                Serial.print(pBuf);
                Serial.println(F("\""));
#endif

#ifdef ENABLE_DELETE_POP
                // Delete the just read message.
#ifdef DEBUG_POP
                Serial.print(F("command DELE "));
                Serial.println(nMailNumber);
#endif    
                client.print(F("DELE "));
                client.println(nMailNumber);
                if (!readOk(true))
                    return -108;
#endif

                // Everything is okay, the mail is read and deleted.
                // Ready for now, don't process the remaining emails.
                found_and_ready = true;
            }
            else
            {
#ifdef DEBUG_POP
                Serial.println(F("No ARDUINO keyword in subject"));
#endif        
                // This email has no "Subject: ARDUINO ".
                // But the remaining text has still to be read and disregarded.
                readRemaining();
            }
        }
    }

#ifdef DEBUG_POP
    Serial.println(F("Sending QUIT"));
#endif  
    client.println(F("QUIT"));

    // After "QUIT", the server still respons with "+OK",
    // but after that, the connection might get lost.
    // So don't read everything after "+OK" (use parameter 'false' for readOk).
    if (!readOk(false))
        return -109;

    client.stop();

#ifdef DEBUG_POP
    Serial.println(F("normally disconnected"));
#endif

    return(nBytes);
}


// Read the response from the mail server.
// That is "+OK" if everything is okay.
// Parameter 'readAll' is to read every character after the "+OK".
boolean readOk(boolean readAll)
{
    // Check the response "+OK" from the mail server
    // In most cases that is followed by a space and more text, but not always.
    // In case of an error the text "-ERR" is received.

    int loopCount = 0;
    char bufOk[4];
    Serial.print("Connected: ");
    Serial.println(client.connected());

    Serial.print("Client Available: ");
    Serial.println(client.available());
    // Wait for response of mail server, with a timout.
    while (!client.available())
    {
        delay(1);
        loopCount++;

        // if nothing received for 10 seconds, timeout
        if (loopCount > 10000)
        {
            client.stop();
#ifdef DEBUG_POP
            Serial.println(F("\nTimeout"));
#endif      
            return false;
        }
    }

    // Read the first three bytes.
    client.readBytes(bufOk, 3);

#ifdef DEBUG_POP
    Serial.write(bufOk, 3);
    Serial.println();
#endif

    // Is it "+OK" ?
    if (strncmp(bufOk, "+OK", 3) != 0)
    {
        popFail();
        return false;
    }

    // When the text after "+OK" is not needed, everything
    // else can be read and disregarded 
    // (or shown in the serial monitor during debugging).
    if (readAll)
        readRemaining();

    return true;
}


void readRemaining()
{
    // This function is called after checking the "+OK".
    // It reads everything from the server, until no more text is
    // available.

    while (client.available())
    {
        char c = client.read();
#ifdef DEBUG_POP_EXTRA   
        Serial.print(c);
#endif    
    }
    return;
}


void popFail()
{
    int loopCount = 0;

#ifdef DEBUG_POP
    Serial.println(F("popFail"));
#endif

    while (!client.available())
    {
        delay(1);
        loopCount++;

        // if nothing received for 10 seconds, timeout
        if (loopCount > 10000)
        {
            client.stop();
#ifdef DEBUG_POP
            Serial.println(F("\nTimeout"));
#endif      
            return;
        }
    }

    client.stop();

#ifdef DEBUG_POP
    Serial.println(F("disconnected due to fail"));
#endif  
} 

Output:

Arduino POP3 email reader
Ethernet started.
Local IP = 192.168.1.117
Press 'c' to check mail.
connected
Connected: 1
Client Active: 0

Timeout
Email POP3 failed, error = -102
1
  • Your title says it's about sending mail, while the code only contains code for receiving mail
    – Gerben
    May 13 '15 at 14:20
1

Hotmail doesn't provide a non-encrypted POP3 service, which would normally run on port 110.

You have no choice but to use the SSL service. However, you can't. Or not easily. SSL is quite a heavy protocol and it is highly doubtful the little Uno is capable of even coming close to being able to work with it.

The Due might have a chance, but again, unlikely.

You would be best off using a computer (server) to check the emails for you and provide a "digest" of information to the Uno instead.

1
  • A Due certainly has the resources (memory) to do it, though the challenge may be in finding/creating an implementation. Something with networking on board and an existing library might be easier and more cost effective - for example, a raspberry pi (though at the cost of a more delicate writable-filesystem OS installation). The idea of using an additional server as a proxy to help out is a good one though, if the unsecured nature of the communication to/from that server is acceptable. May 13 '15 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.