Email POP3

The Arduino with Ethernet shield can be used to retrieve emails using the POP3 protocol.
To send and email with SMTP, see Email.

The sketch below has a specific use.
First of all, a mail server is needed that allows non-encrypted data. The sketch searches the mail for the subject "ARDUINO " followed by a command. The rest of the email is ignored.
Set the mail server in the sketch, with name and password.
Send a mail with the subject "ARDUINO L=1" and when the Arduino reads it, the system led is turned on.

WARNING! Each device on a network must have a unique mac address. If you are using more than one ethernet shield on a network, you must be sure all mac addresses are unique. No duplicates!

  1. //
  2. // Email POP3 reader.
  3. // ------------------
  4. // The Arduino checks the emails for a Subject that starts with "ARDUINO ".
  5. // The text after that is the command for the Arduino.
  6. // That email is delected from the mail server.
  7. //  
  8. // Purpose:
  9. //   Arduino reads mail from a mail server with POP3.
  10. //   This makes it possible to send an email with commands for the Arduino.
  11. //   The Arduino itself is not the mail server,
  12. //   but a mail server is used in between the sender and the Arduino.
  13. //
  14. //   This sketch checks the mail at a command from the serial port.
  15. //   In a normal situation, the mail could be checked by the Arduino every few minutes.
  16. //  
  17. //   When a mailserver is used that is available from the internet, the Arduino
  18. //   can get commands without the need to open a port in the router.
  19. //   A mailserver without encryption is needed.
  20. //
  21. // Usage:
  22. //   Send a mail to a mail server with "ARDUINO " at the begin of the Subject
  23. //   (capital characters ARDUINO, with space at end).
  24. //   After that the command for the Arduino (still in the Subject).
  25. //   This examples sketch turns on the system led with: "ARDUINO L=1".
  26. //
  27. // Restrictions:
  28. //   Only the "Subject" is used, not the body.
  29. //   Only non-encrypted communication is used.
  30. //   The mail is searched for the Subject with the keyword "ARDUINO ",
  31. //   and that email is also deleted. Any other mails for the Arduino will be handled the next time.
  32. //   The timeout for the client is altered and restored to the default of 1 second.
  33. //   The "TOP" command might not be implemented by very old or very simple mail servers.
  34. //
  35. // Versions:
  36. //   Version 1.00, november 2014, by Peter_n, Public Domain.
  37. //     First attempt.
  38. //     With help of MartinCoolen project at:
  39. //       http://fritzing.org/projects/pop3-email-checker
  40. //     Using SurferTim sketches as guidelines:
  41. //       http://playground.arduino.cc/Code/Email
  42. //       http://playground.arduino.cc/Code/WebServerST
  43. //     And some help about the commands:
  44. //       http://kewl.lu/articles/pop3/
  45. //   Version 1.01, november 2014, by Peter_n, Public Domain.
  46. //     The "TOP" command is used to scan the emails.
  47. //     The keyword "ARDUINO " must be used in the Subject,
  48. //     to prevent that other emails are deleted.
  49. //
  50. //
  51.  
  52. #include <SPI.h>
  53. #include <Ethernet.h>
  54.  
  55.  
  56. // Use comments to enable or disable this define for debug messages
  57. #define DEBUG_POP
  58.  
  59. // Use comments to enable or disable the deleting of the mail
  60. #define ENABLE_DELETE_POP
  61.  
  62.  
  63. // The mac address must be an unique number
  64. // A mac generator is used:
  65. //   http://www.miniwebtool.com/mac-address-generator/
  66. byte mac[] = { 0xDE, 0x36, 0x5F, 0x0A, 0x19, 0x83 };  
  67. // change network settings to yours
  68. IPAddress ip( 192, 168, 2, 2 );    
  69. IPAddress gateway( 192, 168, 2, 1 );
  70. IPAddress subnet( 255, 255, 255, 0 );
  71.  
  72.  
  73. // Set the server POP3 address, the port, the user and password.
  74. // The POP3 mail server is something like this:
  75. //     mail.yourdomain.com, pop.yourdomain.com, pop3.yourdomain.com
  76. // Using PROGMEM for these causes a fail when trying to connect and log in.
  77. const char pop_server[] = "mail.yourdomain.com";
  78. const int  pop_port = 110;
  79. const char pop_user[] = "user";
  80. const char pop_pass[] = "****";
  81.  
  82.  
  83. // The number of milliseconds timeout for parseInt() and find().
  84. // The response time for the Server can still be 10 seconds.
  85. #define POP_TIMEOUT 10
  86. #define POP_TIMEOUT_DEFAULT 1000
  87.  
  88.  
  89. EthernetClient client;
  90.  
  91.  
  92. void setup()
  93. {
  94.   Serial.begin( 9600);
  95.   Serial.println(F( "\nArduino POP3 email reader"));
  96.  
  97.   pinMode( 13, OUTPUT);     // the system led is used for testing
  98.  
  99.   // When the Ethernet Shield is used, there is also a SD card connected
  100.   // to the SPI bus. Disable the SD card with chip select at pin 4.
  101.   pinMode( 4, OUTPUT);
  102.   digitalWrite( 4, HIGH);
  103.  
  104.  
  105.   // Start Ethernet. Use only the 'mac' parameter for DHCP
  106.   // Use 'mac' and 'ip' parameters for static IP address.
  107.   //  Ethernet.begin( mac, ip);
  108.   //  Ethernet.begin( mac, ip, gateway, gateway, subnet);
  109.   if( Ethernet.begin( mac) == 0)
  110.   {
  111.     Serial.println("Failed to configure Ethernet using DHCP.");
  112.     // no point in carrying on, so do nothing forevermore:
  113.     while(1);
  114.   }
  115.  
  116.   // print your local IP address.
  117.   Serial.println(F( "Ethernet started."));
  118.   Serial.print(F( "Local IP = "));
  119.   Serial.println(Ethernet.localIP());
  120.  
  121.   Serial.println(F( "Press 'c' to check mail."));
  122. }
  123.  
  124.  
  125. void loop()
  126. {
  127.   // Create a buffer to receive the commands in (that is the Subject of the mail).
  128.   char buffer[32];
  129.  
  130.   byte inChar = Serial.read();
  131.   if(inChar == 'c')
  132.   {
  133.     // The getEmail gets the text of the mail Subject into the buffer.
  134.     // The valid number of received characters are returned.
  135.     // If the return value is < 0, it is an error.
  136.     int n = getEmail( buffer, sizeof(buffer));
  137.  
  138.     if( n<0)
  139.     {
  140.       Serial.print(F("Email POP3 failed, error = "));
  141.       Serial.println( n);
  142.     }
  143.     else
  144.     {
  145.       if( n == 0)
  146.       {
  147.         Serial.println(F("Ready, nothing to do."));
  148.       }
  149.       else
  150.       {
  151.         // 'n' is > 0, a command received.
  152.         Serial.print(F("Email checked, Command = \""));
  153.         Serial.print( buffer);
  154.         Serial.println(F("\""));
  155.  
  156.  
  157.         // Check the commands.
  158.         //
  159.         // At this moment, a single command 'L' is used to set system led on or off.
  160.         //    L=1   (set led on)
  161.         //    L=0   (set led off)
  162.         if( buffer[0] == 'L' && buffer[1] == '=')
  163.         {
  164.           digitalWrite( 13, buffer[2] == '0' ? LOW : HIGH);
  165.         }
  166.       }
  167.     }
  168.   }
  169. }
  170.  
  171.  
  172. // getEmail
  173. // --------
  174. // Find an email on a mail server, using POP3.
  175. // The Subject should start with "ARDUINO " and the text
  176. // after that is copied into pBuf.
  177. //
  178. // The data in pBuf is only valid if the return value is not an error
  179. // (an error is return value less than zero).
  180. //
  181. int getEmail( char *pBuf, int nBufSize)
  182. {
  183.   // nBytes is the number of bytes that is returned by getEmail.
  184.   int nBytes = 0;
  185.  
  186.   // Connect to server
  187.   // client.connect returns '1' if okay, or negative number if error.
  188.   //    SUCCESS 1
  189.   //    0     (error, unknown timeout, perhaps an error in the library)
  190.   //    TIMED_OUT -1
  191.   //    INVALID_SERVER -2
  192.   //    TRUNCATED -3
  193.   //    INVALID_RESPONSE -4
  194.   //    -5    (there is no mail server at that IP address and port)
  195.   // The string for the server must be a normal string in sram, no PROGMEM allowed.
  196.   int nError = client.connect( pop_server, pop_port);
  197.  
  198.   // During testing, a value of zero was sometimes returned.
  199.   // This is not according to the documentation and it is an error.
  200.   // Therefor the non-error value '0' is turned into a negative number to
  201.   // indicate an error.
  202.   if( nError == 0)
  203.     return( -200);
  204.  
  205.   // Only a value of 1 is okay.
  206.   if( nError != 1)
  207.     return( nError);
  208.  
  209. #ifdef DEBUG_POP
  210.   Serial.println(F("connected"));
  211. #endif
  212.  
  213.   // The server should respond with "+OK" and maybe more text after that.
  214.   // Check if "+OK" can be read.
  215.   // The parameter 'true' is to read also everything after the "+OK".
  216.   if(!readOk( true))
  217.     return -102;
  218.  
  219. #ifdef DEBUG_POP
  220.   Serial.println(F("command USER"));
  221. #endif
  222.   client.print(F( "USER "));
  223.   client.println( pop_user);
  224.   if(!readOk( true))
  225.     return -103;
  226.  
  227. #ifdef DEBUG_POP
  228.   Serial.println(F("command PASS"));
  229. #endif
  230.   client.print(F( "PASS "));
  231.   client.println( pop_pass);
  232.   if(!readOk( true))
  233.     return -104;
  234.  
  235. #ifdef DEBUG_POP
  236.   Serial.println(F("command STAT"));
  237. #endif  
  238.   client.println(F( "STAT"));
  239.   if(!readOk( false))
  240.     return -105;
  241.  
  242.   // The whole line was like this: "+OK 3 15343"
  243.   // It means that 3 emails are waiting with a total size of 15343.
  244.   // At this moment, the "+OK" is read, but nothing else.
  245.   // Check if there is a space after "+OK".
  246.   char c = client.read();
  247.   if( c != ' ')
  248.     return -106;
  249.  
  250.   client.setTimeout( POP_TIMEOUT);       // set timeout lower for parseInt
  251.   // Read the number of emails that are on the server.
  252.   int nMails = client.parseInt();
  253.   client.setTimeout( POP_TIMEOUT_DEFAULT);  // restore timeout to 1 second
  254.  
  255.   // Read the remaining of the response to STAT.
  256.   readRemaining();
  257.  
  258. #ifdef DEBUG_POP
  259.   Serial.print(F( "Number of emails="));
  260.   Serial.println( nMails);
  261. #endif
  262.  
  263.   // Test if there are emails waiting.
  264.   if( nMails == 0)
  265.   {
  266.     // No emails, but no error. Set buffer to empty string.
  267.     nBytes = 0;               // the returned value
  268.     pBuf[0] = '\0';           // set empty string
  269.   }
  270.   else if( nMails > 0)
  271.   {
  272.     // emails are waiting.
  273.     // Scan the emails until the first is found with the keyword "ARDUINO " at the
  274.     // beginning of the "Subject: ".
  275.  
  276.     boolean found_and_ready = false;
  277.     for( int nMailNumber = 1; nMailNumber <= nMails && !found_and_ready; nMailNumber++)
  278.     {
  279.       // The command RETR <x> gets the whole mail.
  280.       // The command TOP <x> <size> gets the header plus 'size' of the body.
  281.  
  282. #ifdef DEBUG_POP
  283.       Serial.print(F( "command TOP "));
  284.       Serial.print( nMailNumber);
  285.       Serial.println(F( " 0"));
  286. #endif    
  287.       client.print(F( "TOP "));
  288.       client.print( nMailNumber);
  289.       client.println(F( " 0"));
  290.  
  291.       // Use readOk with parameter 'false' to stop reading after "+OK".
  292.       if(!readOk( false))
  293.         return -107;
  294.       // The header of the email is waiting to be read, use the Stream.find() to look for the Subject.
  295.       // The text "Subject: " should be at the beginning of a line, but that is not tested.
  296.       // The first found text "Subject: " is assumed to be the real subject.
  297.       // I have checked many years of emails, and the text is always "Subject: ", and never "SUBJECT: ".
  298.       // At the moment, it is not possible to use the F() macro for Stream.find
  299.       // Only the email that starts with "ARDUINO " at the start of the Subject is used.
  300.  
  301.       client.setTimeout( POP_TIMEOUT);      // set short timeout for find().
  302.       // find() returns true if found and false if not.
  303.       boolean foundsubject = client.find( "Subject: ARDUINO ");
  304.       client.setTimeout( POP_TIMEOUT_DEFAULT);   // restore timeout to 1 second
  305.  
  306.       if( foundsubject)
  307.       {
  308. #ifdef DEBUG_POP
  309.         Serial.println(F("Found an email for me"));
  310. #endif        
  311.  
  312.         // Read the remaining subject (that is the command for the Arduino) into a buffer.
  313.         // Every line from the mail server should end with CR + LF,
  314.         // but to be sure, both CR and LF are checked.
  315.         // Alternative:
  316.         //    client.readBytesUntil('\r', pBuf, nBufSize);
  317.  
  318.         // The last position in the buffer is reserved for the zero terminator.
  319.         // So read data until (nBufSize-1).
  320.         int i;
  321.         for( i = 0; i < (nBufSize-1) && client.available(); i++)
  322.         {
  323.           char c = client.read();
  324.           if (c == '\r' || c == '\n')
  325.             break;
  326.           pBuf[i] = c;
  327.         }
  328.         // Add zero terminator
  329.         pBuf[i] = '\0';
  330.         nBytes = i;     // the number of received bytes is returned by the getEmail() function.
  331.  
  332.         // More text of the header could be following the Subject.
  333.         // That is read and disregarded.
  334.         readRemaining();
  335.  
  336. #ifdef DEBUG_POP
  337.         Serial.print(F( "Subject = \"ARDUINO "));
  338.         Serial.print( pBuf);
  339.         Serial.println(F( "\""));
  340. #endif
  341.  
  342. #ifdef ENABLE_DELETE_POP
  343.         // Delete the just read message.
  344. #ifdef DEBUG_POP
  345.         Serial.print(F( "command DELE "));
  346.         Serial.println( nMailNumber);
  347. #endif    
  348.         client.print(F( "DELE "));
  349.         client.println( nMailNumber);
  350.         if(!readOk( true))
  351.           return -108;
  352. #endif
  353.  
  354.         // Everything is okay, the mail is read and deleted.
  355.         // Ready for now, don't process the remaining emails.
  356.         found_and_ready = true;
  357.       }
  358.       else
  359.       {
  360. #ifdef DEBUG_POP
  361.         Serial.println(F("No ARDUINO keyword in subject"));
  362. #endif        
  363.         // This email has no "Subject: ARDUINO ".
  364.         // But the remaining text has still to be read and disregarded.
  365.         readRemaining();
  366.       }
  367.     }
  368.   }
  369.  
  370. #ifdef DEBUG_POP
  371.   Serial.println(F( "Sending QUIT"));
  372. #endif  
  373.   client.println(F( "QUIT"));
  374.  
  375.   // After "QUIT", the server still respons with "+OK",
  376.   // but after that, the connection might get lost.
  377.   // So don't read everything after "+OK" (use parameter 'false' for readOk).
  378.   if(!readOk( false))
  379.     return -109;
  380.  
  381.   client.stop();
  382.  
  383. #ifdef DEBUG_POP
  384.   Serial.println(F("normally disconnected"));
  385. #endif
  386.  
  387.   return( nBytes);
  388. }
  389.  
  390.  
  391. // Read the response from the mail server.
  392. // That is "+OK" if everything is okay.
  393. // Parameter 'readAll' is to read every character after the "+OK".
  394. boolean readOk( boolean readAll)
  395. {
  396.   // Check the response "+OK" from the mail server
  397.   // In most cases that is followed by a space and more text, but not always.
  398.   // In case of an error the text "-ERR" is received.
  399.  
  400.   int loopCount = 0;
  401.   char bufOk[4];
  402.  
  403.   // Wait for response of mail server, with a timout.
  404.   while(!client.available())
  405.   {
  406.     delay(1);
  407.     loopCount++;
  408.  
  409.     // if nothing received for 10 seconds, timeout
  410.     if(loopCount > 10000)
  411.     {
  412.       client.stop();
  413. #ifdef DEBUG_POP
  414.       Serial.println(F("\nTimeout"));
  415. #endif      
  416.       return false;
  417.     }
  418.   }
  419.  
  420.   // Read the first three bytes.
  421.   client.readBytes(bufOk, 3);
  422.  
  423. #ifdef DEBUG_POP
  424.   Serial.write(bufOk, 3);
  425.   Serial.println();
  426. #endif
  427.  
  428.   // Is it "+OK" ?
  429.   if( strncmp( bufOk, "+OK", 3) != 0)
  430.   {
  431.     popFail();
  432.     return false;
  433.   }
  434.  
  435.   // When the text after "+OK" is not needed, everything
  436.   // else can be read and disregarded
  437.   // (or shown in the serial monitor during debugging).
  438.   if( readAll)
  439.     readRemaining();
  440.  
  441.   return true;
  442. }
  443.  
  444.  
  445. void readRemaining()
  446. {
  447.   // This function is called after checking the "+OK".
  448.   // It reads everything from the server, until no more text is
  449.   // available.
  450.  
  451.   while(client.available())
  452.   {
  453.     char c = client.read();
  454. #ifdef DEBUG_POP_EXTRA  
  455.     Serial.print( c);
  456. #endif    
  457.   }
  458.   return;
  459. }
  460.  
  461.  
  462. void popFail()
  463. {
  464.   int loopCount = 0;
  465.  
  466. #ifdef DEBUG_POP
  467.   Serial.println(F("popFail"));
  468. #endif
  469.  
  470.   while(!client.available())
  471.   {
  472.     delay(1);
  473.     loopCount++;
  474.  
  475.     // if nothing received for 10 seconds, timeout
  476.     if(loopCount > 10000)
  477.     {
  478.       client.stop();
  479. #ifdef DEBUG_POP
  480.       Serial.println(F("\nTimeout"));
  481. #endif      
  482.       return;
  483.     }
  484.   }
  485.  
  486.   client.stop();
  487.  
  488. #ifdef DEBUG_POP
  489.   Serial.println(F("disconnected due to fail"));
  490. #endif  
  491. }

Share