├── .gitignore
├── LICENSE
├── README.md
├── ESP32_FTPClient.ino
├── ESP32_FTPClient.h
└── ESP32_FTPClient.cpp
/.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 |
--------------------------------------------------------------------------------
/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 | [](https://github.com/blackcodetavern/ESP32_FTPClient/blob/master/LICENSE)
4 | [](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 |
24 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------