├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── examples └── ESP8266SMTP_Gmail │ └── ESP8266SMTP_Gmail.ino ├── keywords.txt ├── library.properties └── src ├── ESP8266SMTP.cpp └── ESP8266SMTP.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Boris Shobat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266SMTP 2 | 3 | SMTP email library for ESP8266 based boards programmed in Arduino environment. 4 | 5 | ## Table of Contents 6 | 1. [Overview](#section-o) 7 | 2. [Dependency](#section-d) 8 | 3. [Usage](#section-u) 9 | 10 | ## [Overview](id:section-o) 11 | 12 | The library uses ESP8266 Arduino libraries to send emails with smtp servers like Gmail. 13 | 14 | ## [Dependency](id:section-d) 15 | - Arduino IDE [Arduino website](http://www.arduino.cc/en/main/software). 16 | - Arduino core for ESP8266 WiFi chip: [https://github.com/esp8266/Arduino](https://github.com/esp8266/Arduino) 17 | 18 | ## [Usage](id:section-u) 19 | See example 20 | -------------------------------------------------------------------------------- /examples/ESP8266SMTP_Gmail/ESP8266SMTP_Gmail.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char* ssid = ""; // WIFI network name 5 | const char* password = ""; // WIFI network password 6 | uint8_t connection_state = 0; // Connected to WIFI or not 7 | uint16_t reconnect_interval = 10000; // If not connected wait time to try again 8 | 9 | uint8_t WiFiConnect(const char* ssID, const char* nPassword) 10 | { 11 | static uint16_t attempt = 0; 12 | Serial.print("Connecting to "); 13 | Serial.println(ssID); 14 | WiFi.begin(ssID, nPassword); 15 | 16 | uint8_t i = 0; 17 | while(WiFi.status() != WL_CONNECTED && i++ < 50) { 18 | delay(200); 19 | Serial.print("."); 20 | } 21 | ++attempt; 22 | Serial.println(""); 23 | if(i == 51) { 24 | Serial.print(F("Connection: TIMEOUT on attempt: ")); 25 | Serial.println(attempt); 26 | if(attempt % 2 == 0) 27 | Serial.println(F("Check if access point available or SSID and Password are correct\r\n")); 28 | return false; 29 | } 30 | Serial.println(F("Connection: ESTABLISHED")); 31 | Serial.print(F("Got IP address: ")); 32 | Serial.println(WiFi.localIP()); 33 | return true; 34 | } 35 | 36 | void Awaits(uint16_t interval) 37 | { 38 | uint32_t ts = millis(); 39 | while(!connection_state){ 40 | delay(50); 41 | if(!connection_state && millis() > (ts + interval)){ 42 | connection_state = WiFiConnect(ssid, password); 43 | ts = millis(); 44 | } 45 | } 46 | } 47 | 48 | void setup() 49 | { 50 | Serial.begin(115200); 51 | delay(2000); 52 | 53 | connection_state = WiFiConnect(ssid, password); 54 | 55 | if(!connection_state) { // if not connected to WIFI 56 | Awaits(reconnect_interval); // constantly trying to connect 57 | } 58 | 59 | uint32_t startTime = millis(); 60 | 61 | SMTP.setEmail("YOUR_EMAIL_ADDRESS@gmail.com") 62 | .setPassword("YOUR_PASSWORD") 63 | .Subject("ESP8266SMTP Gmail test") 64 | .setFrom("ESP8266SMTP") 65 | .setForGmail(); // simply sets port to 465 and setServer("smtp.gmail.com"); 66 | // message text from http://www.blindtextgenerator.com/lorem-ipsum 67 | if(SMTP.Send("Recipient Email, use comma for multiple recipients", "The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words. Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators. To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words. If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages. The new common language will be more simple and regular than the existing European languages. It will be as simple as Occidental; in fact, it will be Occidental. To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words. Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators. To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words. If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages. The new common language will be more simple and regular than the existing European languages. It will be as simple as Occidental; in fact, it will be Occidental. To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words. Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators. To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words. If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages. The new common language will be more simple and regular than the existing European languages. It will be as simple as Occidental; in fact, it will be Occidental. To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is.The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words. Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators. To achieve this, it would be necessary to have uniform grammar, pronunciation and more common ")) { 68 | Serial.println(F("Message sent")); 69 | } else { 70 | Serial.print(F("Error sending message: ")); 71 | Serial.println(SMTP.getError()); 72 | } 73 | 74 | Serial.println(millis() - startTime); 75 | } 76 | 77 | void loop() 78 | {} 79 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map ESP8266SMTP 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SMTP KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | setPort KEYWORD2 16 | setServer KEYWORD2 17 | Subject KEYWORD2 18 | setFrom KEYWORD2 19 | setEmail KEYWORD2 20 | setPassword KEYWORD2 21 | setForGmail KEYWORD2 22 | getBase64Email KEYWORD2 23 | getBase64Password KEYWORD2 24 | getLastResponce KEYWORD2 25 | getError KEYWORD2 26 | Send KEYWORD2 27 | setForGmail KEYWORD2 28 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP8266SMTP 2 | version=2.0.2 3 | author=Boris Shobat 4 | maintainer=Boris Shobat 5 | sentence=SMTP email library. 6 | paragraph=Send simple email via ESP8266 based boards using smtp servers like Gmail. 7 | category=Communication 8 | url=https://github.com/CosmicBoris/ESP8266SMTP 9 | architectures=esp8266 -------------------------------------------------------------------------------- /src/ESP8266SMTP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | ESP8266SMTPHelper::ESP8266SMTPHelper(const char* login, const char* password) : 5 | _base64_login(strdup(login)), 6 | _base64_password(strdup(password)){} 7 | 8 | ESP8266SMTPHelper::~ESP8266SMTPHelper() 9 | { 10 | if(_subject) delete[] _subject; 11 | if(_from) delete[] _from; 12 | if(_base64_login) delete[] _base64_login; 13 | if(_base64_password) delete[] _base64_password; 14 | 15 | } 16 | 17 | ESP8266SMTPHelper& ESP8266SMTPHelper::Subject(const char* s) 18 | { 19 | if(_subject) 20 | delete[] _subject; 21 | 22 | _subject = strdup(s); 23 | return *this; 24 | } 25 | 26 | ESP8266SMTPHelper& ESP8266SMTPHelper::setPort(uint16_t port) 27 | { 28 | _smtp_port = port; 29 | return *this; 30 | } 31 | 32 | ESP8266SMTPHelper& ESP8266SMTPHelper::setServer(const char *server) 33 | { 34 | if(_smtp_server) 35 | delete[] _smtp_server; 36 | 37 | _smtp_server = strdup(server); 38 | return *this; 39 | } 40 | 41 | ESP8266SMTPHelper& ESP8266SMTPHelper::setFrom(const char* from) 42 | { 43 | if(_from) 44 | delete[] _from; 45 | 46 | _from = strdup(from); 47 | return *this; 48 | } 49 | 50 | ESP8266SMTPHelper& ESP8266SMTPHelper::setEmail(const char* m) 51 | { 52 | if(_emailAddress) 53 | delete[] _emailAddress; 54 | if(_base64_login) 55 | delete[] _base64_login; 56 | _emailAddress = strdup(m); 57 | _base64_login = strdup(base64::encode(m).c_str()); 58 | 59 | return *this; 60 | } 61 | 62 | ESP8266SMTPHelper& ESP8266SMTPHelper::setPassword(const char* password) 63 | { 64 | if(_base64_password) 65 | delete[] _base64_password; 66 | 67 | _base64_password = strdup(base64::encode(password).c_str()); 68 | return *this; 69 | } 70 | 71 | ESP8266SMTPHelper& ESP8266SMTPHelper::setForGmail() 72 | { 73 | _smtp_port = 465; 74 | setServer("smtp.gmail.com"); 75 | return *this; 76 | } 77 | 78 | char* ESP8266SMTPHelper::getBase64Email() 79 | { 80 | return _base64_login; 81 | } 82 | 83 | char* ESP8266SMTPHelper::getBase64Password() 84 | { 85 | return _base64_password; 86 | } 87 | 88 | const char* ESP8266SMTPHelper::getError() 89 | { 90 | return _error; 91 | } 92 | 93 | String ESP8266SMTPHelper::getLastResponce() 94 | { 95 | return _serverResponce; 96 | } 97 | 98 | bool ESP8266SMTPHelper::AwaitSMTPResponse(WiFiClientSecure &client, const String &resp, uint16_t timeOut) 99 | { 100 | uint32_t timeAwait = millis() + timeOut; 101 | while(!client.available()) { 102 | if(millis() > timeAwait) { 103 | _error = "SMTP Response TIMEOUT!"; 104 | return false; 105 | } 106 | } 107 | _serverResponce = client.readStringUntil('\n'); 108 | #if defined(GS_SERIAL_LOG_LEVEL_1) || defined(GS_SERIAL_LOG_LEVEL_2) 109 | Serial.println(_serverResponce); 110 | #endif 111 | return !resp || _serverResponce.indexOf(resp) != -1; 112 | } 113 | 114 | bool ESP8266SMTPHelper::Send(const String &to, const String &message) 115 | { 116 | if(!_smtp_server) { 117 | _error = "SMTP server not set."; 118 | return false; 119 | } 120 | 121 | WiFiClientSecure client; 122 | 123 | #if defined(GS_SERIAL_LOG_LEVEL_2) 124 | Serial.print(F("Connecting to: ")); 125 | Serial.println(_smtp_server); 126 | #endif 127 | if(!client.connect(_smtp_server, _smtp_port)) { 128 | _error = "Could not connect to mail server"; 129 | return false; 130 | } 131 | if(!AwaitSMTPResponse(client, "220")) { 132 | _error = "Connection error"; 133 | return false; 134 | } 135 | 136 | #if defined(GS_SERIAL_LOG_LEVEL_2) 137 | Serial.println(FPSTR(SMTP_HELO)); 138 | #endif 139 | client.println(FPSTR(SMTP_HELO)); 140 | if(!AwaitSMTPResponse(client, "250")) { 141 | _error = "identification error"; 142 | return false; 143 | } 144 | 145 | #if defined(GS_SERIAL_LOG_LEVEL_2) 146 | Serial.println(FPSTR(SMTP_AUTH)); 147 | #endif 148 | client.println(FPSTR(SMTP_AUTH)); 149 | AwaitSMTPResponse(client); 150 | 151 | #if defined(GS_SERIAL_LOG_LEVEL_2) 152 | Serial.println(F("BASE64_LOGIN:")); 153 | #endif 154 | client.println(_base64_login); 155 | AwaitSMTPResponse(client); 156 | 157 | #if defined(GS_SERIAL_LOG_LEVEL_2) 158 | Serial.println(F("BASE64_PASSWORD:")); 159 | #endif 160 | client.println(_base64_password); 161 | if(!AwaitSMTPResponse(client, "235")) { 162 | _error = "SMTP AUTH error"; 163 | return false; 164 | } 165 | 166 | String tmp; 167 | tmp.reserve(46); // 14 length of SMTP_FROM + 32 for email, trying to prevent reallocation of string buffer when string changes 168 | tmp = FPSTR(SMTP_FROM); 169 | tmp.replace("$", _emailAddress); 170 | 171 | #if defined(GS_SERIAL_LOG_LEVEL_2) 172 | Serial.println(tmp); 173 | #endif 174 | client.println(tmp); 175 | AwaitSMTPResponse(client); 176 | 177 | bool oneRecepient = to.indexOf(',') == -1; 178 | 179 | if(oneRecepient) { 180 | tmp = FPSTR(SMTP_RCPT); 181 | tmp.replace("$", to); 182 | 183 | #if defined(GS_SERIAL_LOG_LEVEL_2) 184 | Serial.println(tmp); 185 | #endif 186 | client.println(tmp); 187 | AwaitSMTPResponse(client); 188 | } else { 189 | char *toCopy = strdup(to.c_str()); // make copy becouse strtok modifyes original string 190 | char *sz_r = strtok(toCopy, ","); 191 | while(sz_r) { 192 | while(*sz_r == ' ') ++sz_r; // skip spaces after comma. 193 | 194 | tmp = FPSTR(SMTP_RCPT); 195 | tmp.replace("$", sz_r); 196 | 197 | #if defined(GS_SERIAL_LOG_LEVEL_2) 198 | Serial.println(tmp); 199 | #endif 200 | client.println(tmp); 201 | AwaitSMTPResponse(client); 202 | 203 | sz_r = strtok(NULL, ","); 204 | } 205 | delete[] toCopy; 206 | } 207 | 208 | #if defined(GS_SERIAL_LOG_LEVEL_2) 209 | Serial.println("DATA"); 210 | #endif 211 | client.println("DATA"); 212 | if(!AwaitSMTPResponse(client, "354")) { 213 | _error = "SMTP DATA error"; 214 | return false; 215 | } 216 | 217 | tmp = F("From: <$>"); 218 | if(_from != nullptr) 219 | tmp.replace(" ", _from); 220 | tmp.replace("$",_emailAddress); 221 | #if defined(GS_SERIAL_LOG_LEVEL_2) 222 | Serial.println(tmp); 223 | #endif 224 | client.println(tmp); 225 | 226 | tmp = F("To: <$>"); 227 | if(oneRecepient) { 228 | tmp.replace("$", to); 229 | #if defined(GS_SERIAL_LOG_LEVEL_2) 230 | Serial.println(tmp); 231 | #endif 232 | client.println(tmp); 233 | } else { 234 | char *rec = strtok((char*)to.c_str(), ","); 235 | while(rec) { 236 | while(*rec == ' ') ++rec; // skip spaces after comma. 237 | 238 | tmp.replace("$", rec); 239 | #if defined(GS_SERIAL_LOG_LEVEL_2) 240 | Serial.println(tmp); 241 | #endif 242 | client.println(tmp); 243 | rec = strtok(NULL, ","); 244 | tmp = F("Cc: <$>"); 245 | } 246 | } 247 | 248 | client.print(FPSTR(SMTP_SUB)); 249 | client.println(_subject); 250 | client.print(FPSTR(HTML_HEAD)); 251 | client.print(message); 252 | client.println(FPSTR(HTML_END)); 253 | 254 | if(!AwaitSMTPResponse(client, "250")) { 255 | _error = "Sending message error"; 256 | return false; 257 | } 258 | client.println("QUIT"); 259 | if(!AwaitSMTPResponse(client, "221")) { 260 | _error = "SMTP QUIT error"; 261 | return false; 262 | } 263 | return true; 264 | } 265 | 266 | ESP8266SMTPHelper SMTP; -------------------------------------------------------------------------------- /src/ESP8266SMTP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ESP8266SMTPHelper class helps send e-mails 3 | * using Arduino core for ESP8266 WiFi chip 4 | * by Boris Shobat 5 | * May 11 2017 6 | */ 7 | 8 | #ifndef _ESP8266SMTPHelper_H 9 | #define _ESP8266SMTPHelper_H 10 | 11 | #include 12 | 13 | //#define GS_SERIAL_LOG_LEVEL_1 // Print to Serial only server responce 14 | //#define GS_SERIAL_LOG_LEVEL_2 // Print to Serial client commands and server responce 15 | 16 | const char HTML_HEAD[] PROGMEM = "Mime-Version: 1.0\r\nContent-Type: text/html; charset=\"UTF-8\";\r\nContent-Transfer-Encoding: 7bit;\r\n\r\n"; 17 | const char HTML_END[] PROGMEM = "\r\n."; 18 | const char SMTP_HELO[] PROGMEM = "HELO friend"; 19 | const char SMTP_AUTH[] PROGMEM = "AUTH LOGIN"; 20 | const char SMTP_FROM[] PROGMEM = "MAIL FROM:<$>"; 21 | const char SMTP_RCPT[] PROGMEM = "RCPT TO:<$>"; 22 | const char SMTP_SUB[] PROGMEM = "Subject: "; 23 | 24 | class ESP8266SMTPHelper 25 | { 26 | private: 27 | uint16_t _smtp_port = 465; 28 | const char *_error = nullptr; 29 | char 30 | *_smtp_server = nullptr, 31 | *_emailAddress = nullptr, 32 | *_base64_login = nullptr, 33 | *_base64_password = nullptr, 34 | *_from = nullptr, 35 | *_subject = nullptr; 36 | String _serverResponce; 37 | bool AwaitSMTPResponse(WiFiClientSecure &client, const String &resp = "", uint16_t timeOut = 10000); 38 | 39 | public: 40 | ESP8266SMTPHelper() = default; 41 | ESP8266SMTPHelper(const char*, const char*); 42 | ~ESP8266SMTPHelper(); 43 | ESP8266SMTPHelper 44 | &setPort(uint16_t), 45 | &setServer(const char*), 46 | &Subject(const char*), 47 | &setFrom(const char*), 48 | &setEmail(const char*), 49 | &setPassword(const char*), 50 | &setForGmail(); 51 | char* getBase64Email(); 52 | char* getBase64Password(); 53 | 54 | String getLastResponce(); 55 | const char* getError(); 56 | 57 | bool Send(const String &to, const String &message); 58 | }; 59 | 60 | extern ESP8266SMTPHelper SMTP; 61 | #endif 62 | --------------------------------------------------------------------------------