├── .gitignore ├── ESP32_FTPClient.cpp ├── ESP32_FTPClient.h ├── ESP32_FTPClient.ino ├── LICENSE ├── README.md └── octocat.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # PlatformIO 35 | .pio 36 | .pioenvs 37 | .piolibdeps 38 | .vscode 39 | -------------------------------------------------------------------------------- /ESP32_FTPClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ESP32_FTPClient.h" 3 | 4 | ESP32_FTPClient::ESP32_FTPClient(char* _serverAdress, char* _userName, char* _passWord, uint16_t _timeout, uint8_t _verbose){ 5 | userName = _userName; 6 | passWord = _passWord; 7 | serverAdress = _serverAdress; 8 | timeout = _timeout; 9 | verbose = _verbose; 10 | } 11 | 12 | WiFiClient* ESP32_FTPClient::GetDataClient() { 13 | return &dclient; 14 | } 15 | 16 | bool ESP32_FTPClient::isConnected(){ 17 | if(!_isConnected) 18 | { 19 | FTPerr("FTP error: "); 20 | FTPerr(outBuf); 21 | FTPerr("\n"); 22 | } 23 | 24 | return _isConnected; 25 | } 26 | 27 | void ESP32_FTPClient::GetLastModifiedTime(const char * fileName, char* result) { 28 | FTPdbgn("Send MDTM"); 29 | if(!isConnected()) return; 30 | client.print(F("MDTM ")); 31 | client.println(F(fileName)); 32 | GetFTPAnswer (result, 4); 33 | } 34 | 35 | void ESP32_FTPClient::WriteClientBuffered(WiFiClient* cli, unsigned char * data, int dataLength) { 36 | if(!isConnected()) return; 37 | 38 | size_t clientCount = 0; 39 | for(int i = 0; i < dataLength;i++){ 40 | clientBuf[clientCount] = data[i]; 41 | //client.write(data[i]) 42 | clientCount++; 43 | if (clientCount > bufferSize-1) { 44 | cli->write(clientBuf, bufferSize); 45 | clientCount = 0; 46 | } 47 | } 48 | if (clientCount > 0){ 49 | cli->write(clientBuf, clientCount); 50 | } 51 | } 52 | 53 | void ESP32_FTPClient::GetFTPAnswer (char* result, int offsetStart) { 54 | char thisByte; 55 | outCount = 0; 56 | 57 | unsigned long _m = millis(); 58 | while (!client.available() && millis() < _m + timeout) delay(1); 59 | 60 | if( !client.available()){ 61 | memset( outBuf, 0, sizeof(outBuf) ); 62 | strcpy( outBuf, "Offline"); 63 | 64 | _isConnected = false; 65 | isConnected(); 66 | return; 67 | } 68 | 69 | while (client.available()) { 70 | thisByte = client.read(); 71 | if (outCount < sizeof(outBuf)) { 72 | outBuf[outCount] = thisByte; 73 | outCount++; 74 | outBuf[outCount] = 0; 75 | } 76 | } 77 | 78 | if(outBuf[0] == '4' || outBuf[0] == '5' ){ 79 | _isConnected = false; 80 | isConnected(); 81 | return; 82 | } 83 | else 84 | { 85 | _isConnected = true; 86 | } 87 | 88 | if(result != NULL){ 89 | FTPdbgn("Result start"); 90 | // Deprecated 91 | for(int i = offsetStart; iprint(str); 121 | } 122 | 123 | void ESP32_FTPClient::CloseConnection() { 124 | client.println(F("QUIT")); 125 | client.stop(); 126 | FTPdbgn(F("Connection closed")); 127 | } 128 | 129 | void ESP32_FTPClient::OpenConnection() { 130 | FTPdbg(F("Connecting to: ")); 131 | FTPdbgn(serverAdress); 132 | if (client.connect(serverAdress, 21, timeout)) { // 21 = FTP server 133 | FTPdbgn(F("Command connected")); 134 | } 135 | 136 | GetFTPAnswer(); 137 | 138 | FTPdbgn("Send USER"); 139 | client.print(F("USER ")); 140 | client.println(F(userName)); 141 | GetFTPAnswer(); 142 | 143 | FTPdbgn("Send PASSWORD"); 144 | client.print(F("PASS ")); 145 | client.println(F(passWord)); 146 | GetFTPAnswer(); 147 | 148 | FTPdbgn("Send SYST"); 149 | client.println(F("SYST")); 150 | GetFTPAnswer(); 151 | } 152 | 153 | void ESP32_FTPClient::RenameFile(char* from, char* to) { 154 | FTPdbgn("Send RNFR"); 155 | if(!isConnected()) return; 156 | client.print(F("RNFR ")); 157 | client.println(F(from)); 158 | GetFTPAnswer(); 159 | 160 | FTPdbgn("Send RNTO"); 161 | client.print(F("RNTO ")); 162 | client.println(F(to)); 163 | GetFTPAnswer(); 164 | } 165 | 166 | void ESP32_FTPClient::NewFile (const char* fileName) { 167 | FTPdbgn("Send STOR"); 168 | if(!isConnected()) return; 169 | client.print(F("STOR ")); 170 | client.println(F(fileName)); 171 | GetFTPAnswer(); 172 | } 173 | 174 | void ESP32_FTPClient::InitFile(const char* type){ 175 | FTPdbgn("Send TYPE"); 176 | if(!isConnected()) return; 177 | FTPdbgn(type); 178 | client.println(F(type)); 179 | GetFTPAnswer(); 180 | 181 | FTPdbgn("Send PASV"); 182 | client.println(F("PASV")); 183 | GetFTPAnswer(); 184 | 185 | char *tStr = strtok(outBuf, "(,"); 186 | int array_pasv[6]; 187 | for ( int i = 0; i < 6; i++) { 188 | tStr = strtok(NULL, "(,"); 189 | if (tStr == NULL) { 190 | FTPdbgn(F("Bad PASV Answer")); 191 | CloseConnection(); 192 | return; 193 | } 194 | array_pasv[i] = atoi(tStr); 195 | } 196 | unsigned int hiPort, loPort; 197 | hiPort = array_pasv[4] << 8; 198 | loPort = array_pasv[5] & 255; 199 | 200 | FTPdbg(F("Data port: ")); 201 | hiPort = hiPort | loPort; 202 | FTPdbgn(hiPort); 203 | if (dclient.connect(serverAdress, hiPort, timeout)) { 204 | FTPdbgn(F("Data connection established")); 205 | } 206 | } 207 | 208 | void ESP32_FTPClient::AppendFile (char* fileName) { 209 | FTPdbgn("Send APPE"); 210 | if(!isConnected()) return; 211 | client.print(F("APPE ")); 212 | client.println(F(fileName)); 213 | GetFTPAnswer(); 214 | } 215 | 216 | void ESP32_FTPClient::ChangeWorkDir(const char * dir) { 217 | FTPdbgn("Send CWD"); 218 | if(!isConnected()) return; 219 | client.print(F("CWD ")); 220 | client.println(F(dir)); 221 | GetFTPAnswer(); 222 | } 223 | 224 | void ESP32_FTPClient::DeleteFile(const char * file) { 225 | FTPdbgn("Send DELE"); 226 | if(!isConnected()) return; 227 | client.print(F("DELE ")); 228 | client.println(F(file)); 229 | GetFTPAnswer(); 230 | } 231 | 232 | void ESP32_FTPClient::MakeDir(const char * dir) { 233 | FTPdbgn("Send MKD"); 234 | if(!isConnected()) return; 235 | client.print(F("MKD ")); 236 | client.println(F(dir)); 237 | GetFTPAnswer(); 238 | } 239 | 240 | void ESP32_FTPClient::RemoveDir(const char * dir) { 241 | FTPdbgn("Send RMD"); 242 | if(!isConnected()) return; 243 | client.print(F("RMD ")); 244 | client.println(F(dir)); 245 | GetFTPAnswer(); 246 | } 247 | 248 | void ESP32_FTPClient::ContentList(const char * dir, String * list) { 249 | char _resp[ sizeof(outBuf) ]; 250 | uint16_t _b = 0; 251 | 252 | FTPdbgn("Send MLSD"); 253 | if(!isConnected()) return; 254 | client.print(F("MLSD")); 255 | client.println(F(dir)); 256 | GetFTPAnswer(_resp); 257 | 258 | // Convert char array to string to manipulate and find response size 259 | // each server reports it differently, TODO = FEAT 260 | //String resp_string = _resp; 261 | //resp_string.substring(resp_string.lastIndexOf('matches')-9); 262 | //FTPdbgn(resp_string); 263 | 264 | unsigned long _m = millis(); 265 | while( !dclient.available() && millis() < _m + timeout) delay(1); 266 | 267 | while(dclient.available()) 268 | { 269 | if( _b < 128 ) 270 | { 271 | list[_b] = dclient.readStringUntil('\n'); 272 | //FTPdbgn(String(_b) + ":" + list[_b]); 273 | _b++; 274 | } 275 | } 276 | 277 | } 278 | 279 | void ESP32_FTPClient::DownloadString(const char * filename, String &str) { 280 | FTPdbgn("Send RETR"); 281 | if(!isConnected()) return; 282 | client.print(F("RETR ")); 283 | client.println(F(filename)); 284 | 285 | char _resp[ sizeof(outBuf) ]; 286 | GetFTPAnswer(_resp); 287 | 288 | unsigned long _m = millis(); 289 | while( !GetDataClient()->available() && millis() < _m + timeout) delay(1); 290 | 291 | while( GetDataClient()->available() ) 292 | { 293 | str += GetDataClient()->readString(); 294 | } 295 | 296 | } 297 | 298 | void ESP32_FTPClient::DownloadFile(const char * filename, unsigned char * buf, size_t length, bool printUART ) { 299 | FTPdbgn("Send RETR"); 300 | if(!isConnected()) return; 301 | client.print(F("RETR ")); 302 | client.println(F(filename)); 303 | 304 | char _resp[ sizeof(outBuf) ]; 305 | GetFTPAnswer(_resp); 306 | 307 | char _buf[2]; 308 | 309 | unsigned long _m = millis(); 310 | while( !dclient.available() && millis() < _m + timeout) delay(1); 311 | 312 | while(dclient.available()) 313 | { 314 | if( !printUART ) 315 | dclient.readBytes(buf, length); 316 | 317 | else 318 | { 319 | for(size_t _b = 0; _b < length; _b++ ) 320 | { 321 | dclient.readBytes(_buf, 1), 322 | Serial.print(_buf[0], HEX); 323 | } 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /ESP32_FTPClient.h: -------------------------------------------------------------------------------- 1 | class ESP32_FTPClient 2 | { 3 | private: 4 | void WriteClientBuffered(WiFiClient* cli, unsigned char * data, int dataLength); 5 | char outBuf[128]; 6 | unsigned char outCount; 7 | WiFiClient client; 8 | WiFiClient dclient; 9 | uint8_t verbose; 10 | 11 | template 12 | void FTPdbg(T msg) { 13 | if(verbose == 2) Serial.print(msg); 14 | } 15 | 16 | template 17 | void FTPdbgn(T msg) { 18 | if(verbose == 2) Serial.println(msg); 19 | } 20 | 21 | template 22 | void FTPerr(T msg) { 23 | if(verbose == 1 || verbose == 2) Serial.print(msg); 24 | } 25 | 26 | char* userName; 27 | char* passWord; 28 | char* serverAdress; 29 | bool _isConnected = false; 30 | unsigned char clientBuf[1500]; 31 | size_t bufferSize = 1500; 32 | uint16_t timeout = 10000; 33 | WiFiClient* GetDataClient(); 34 | 35 | public: 36 | ESP32_FTPClient(char* _serverAdress, char* _userName, char* _passWord, uint16_t _timeout = 10000, uint8_t _verbose = 1); 37 | void OpenConnection(); 38 | void CloseConnection(); 39 | bool isConnected(); 40 | void NewFile (const char* fileName); 41 | void AppendFile( char* fileName); 42 | void WriteData (unsigned char * data, int dataLength); 43 | void CloseFile (); 44 | void GetFTPAnswer (char* result = NULL, int offsetStart = 0); 45 | void GetLastModifiedTime(const char* fileName, char* result); 46 | void RenameFile(char* from, char* to); 47 | void Write(const char * str); 48 | void InitFile(const char* type); 49 | void ChangeWorkDir(const char * dir); 50 | void DeleteFile(const char * file); 51 | void MakeDir(const char * dir); 52 | void RemoveDir(const char * dir); 53 | void ContentList(const char * dir, String * list); 54 | void DownloadString(const char * filename, String &str); 55 | void DownloadFile(const char * filename, unsigned char * buf, size_t length, bool printUART = false); 56 | }; 57 | -------------------------------------------------------------------------------- /ESP32_FTPClient.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ESP32_FTPClient.h" 4 | #include "octocat.h" 5 | 6 | #define WIFI_SSID "" 7 | #define WIFI_PASS "" 8 | 9 | char ftp_server[] = ""; 10 | char ftp_user[] = ""; 11 | char ftp_pass[] = ""; 12 | 13 | char working_dir[] = "."; 14 | 15 | 16 | ESP32_FTPClient ftp (ftp_server,ftp_user,ftp_pass, 5000, 2); 17 | 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | WiFi.begin(WIFI_SSID, WIFI_PASS); 22 | 23 | Serial.println("Connecting Wifi..."); 24 | while (WiFi.status() != WL_CONNECTED) { 25 | delay(500); 26 | Serial.println("Connecting to WiFi.."); 27 | 28 | } 29 | Serial.println("IP address: "); 30 | 31 | Serial.println(WiFi.localIP()); 32 | 33 | ftp.OpenConnection(); 34 | 35 | // Create the new image file and send the image 36 | ftp.ChangeWorkDir(working_dir); 37 | ftp.InitFile("Type I"); 38 | ftp.NewFile("octocat.jpg"); 39 | ftp.WriteData( octocat_pic, sizeof(octocat_pic) ); 40 | ftp.CloseFile(); 41 | 42 | //Create the file new and write a string into it 43 | ftp.InitFile("Type A"); 44 | ftp.NewFile("hello_world.txt"); 45 | ftp.Write("Hello World"); 46 | ftp.CloseFile(); 47 | 48 | //Append an string to a file (it does not need to exist) 49 | ftp.InitFile("Type A"); 50 | ftp.AppendFile("hello_world_append.txt"); 51 | ftp.Write("Hello World"); 52 | ftp.CloseFile(); 53 | } 54 | 55 | void loop() { 56 | delay(1000); 57 | } 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ldab 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 | # FTP Client for the ESP32 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/blackcodetavern/ESP32_FTPClient/blob/master/LICENSE) 4 | [![GitHub last commit](https://img.shields.io/github/last-commit/blackcodetavern/ESP32_FTPClient.svg?style=social)](https://github.com/blackcodetavern/ESP32_FTPClient) 5 | 6 | # Description 7 | I use this client to write sensordata into a log file and to upload images for my rabbit-webcam. 8 | 9 | I took the changes from ldab. Thank you very much for that. However, I want to keep the directory structure as simple as possible so that the examples can be executed without problems. 10 | 11 | I also noticed some problems with my 8 month old espressif library. You need to update the library for the example to work. 12 | 13 | # TODO 14 | [ ] Better errorhandling 15 | 16 | [ ] Less redundant code 17 | 18 | [ ] Recursive folder delete 19 | 20 | ## Imageupload 21 | * For the uploading example we will use the GitHub Octocat, which binary file is [here](./octocat.h): 22 | 23 | Octocat 24 | --------------------------------------------------------------------------------