Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

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. //       https://fritzing.org/projects/pop3-email-checker
  40. //     Using SurferTim sketches as guidelines:
  41. //       https://playground.arduino.cc/Code/Email
  42. //       https://playground.arduino.cc/Code/WebServerST
  43. //     And some help about the commands:
  44. //       https://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. //   https://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. }