diff --git a/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp b/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp
new file mode 100755
index 000000000..c70d17dcf
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp
@@ -0,0 +1,1132 @@
+/*
+ * FTP SERVER FOR ESP32/ESP8266
+ * based on FTP Server for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200)
+ * based on Jean-Michel Gallego's work
+ * modified to work with esp8266 SPIFFS by David Paiva david@nailbuster.com
+ * 2017: modified by @robo8080 (ported to ESP32 and SD)
+ * 2019: modified by @fa1ke5 (use SD card in SD_MMC mode (No SD lib, SD_MMC lib), and added fully fuctional passive mode ftp server)
+ * 2020: modified by @jmwislez (support generic FS, and re-introduced ESP8266)
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#include "ESPFtpServer.h"
+#include
+
+#ifdef ESP32
+#define F(A) A
+#endif
+
+void FtpServer::begin (String uname, String pword, FS *ufp) {
+
+ if (is_up) return;
+
+ cfsp = ufp;
+
+ // Tells the ftp server to begin listening for incoming connection
+ _FTP_USER = uname;
+ _FTP_PASS = pword;
+
+ ftpServer = new WiFiServer(FTP_CTRL_PORT);
+ if (!ftpServer) return;
+ dataServer = new WiFiServer(FTP_DATA_PORT_PASV);
+ if (!dataServer) return;
+
+ ftpServer->begin ();
+ delay (10);
+ dataServer->begin ();
+ delay (10);
+ millisTimeOut = (uint32_t)FTP_TIME_OUT * 60 * 1000;
+ millisDelay = 0;
+ cmdStatus = 0;
+ iniVariables ();
+ is_up = true;
+}
+
+FtpServer::~FtpServer(void) {
+ ftpServer->close();
+ ftpServer->stop();
+ delete ftpServer;
+ dataServer->close();
+ dataServer->stop();
+ delete dataServer;
+}
+
+
+void FtpServer::iniVariables () {
+ // Default for data port
+ dataPort = FTP_DATA_PORT_PASV;
+
+ // Default Data connection is Active
+ dataPassiveConn = false;
+
+ // Set the root directory
+ strcpy (cwdName, "/");
+
+ rnfrCmd = false;
+ transferStatus = 0;
+}
+
+void FtpServer::handleFTP () {
+
+ if (!is_up) return;
+
+ if ((int32_t) (millisDelay - millis ()) > 0) {
+ return;
+ }
+
+ if (ftpServer->hasClient ()) {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> disconnecting client"));
+ #endif
+ client.stop ();
+ client = ftpServer->available ();
+ }
+
+ if (cmdStatus == 0) {
+ if (client.connected ()) {
+ disconnectClient ();
+ }
+ cmdStatus = 1;
+ }
+ else if (cmdStatus == 1) { // Ftp server waiting for connection
+ abortTransfer ();
+ iniVariables ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> ftp server waiting for connection on port ") + String (FTP_CTRL_PORT));
+ #endif
+ cmdStatus = 2;
+ }
+ else if (cmdStatus == 2) { // Ftp server idle
+ if (client.connected ()) { // A client connected
+ clientConnected ();
+ millisEndConnection = millis () + 10 * 1000; // wait client id during 10 s.
+ cmdStatus = 3;
+ }
+ }
+ else if (readChar () > 0) { // got response
+ if (cmdStatus == 3) { // Ftp server waiting for user identity
+ if (userIdentity ()) {
+ cmdStatus = 4;
+ }
+ else {
+ cmdStatus = 0;
+ }
+ }
+ else if (cmdStatus == 4) { // Ftp server waiting for user registration
+ if (userPassword ()) {
+ cmdStatus = 5;
+ millisEndConnection = millis () + millisTimeOut;
+ }
+ else {
+ cmdStatus = 0;
+ }
+ }
+ else if (cmdStatus == 5) { // Ftp server waiting for user command
+ if (!processCommand ()) {
+ cmdStatus = 0;
+ }
+ else {
+ millisEndConnection = millis () + millisTimeOut;
+ }
+ }
+ }
+ else if (!client.connected () || !client) {
+ cmdStatus = 1;
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected"));
+ #endif
+ }
+
+ if (transferStatus == 1) { // Retrieve data
+ if (!doRetrieve ()) {
+ transferStatus = 0;
+ }
+ }
+ else if (transferStatus == 2) { // Store data
+ if (!doStore ()) {
+ transferStatus = 0;
+ }
+ }
+ else if (cmdStatus > 2 && ! ((int32_t) (millisEndConnection - millis ()) > 0 )) {
+ client.println (F("530 Timeout"));
+ millisDelay = millis () + 200; // delay of 200 ms
+ cmdStatus = 0;
+ }
+}
+
+void FtpServer::clientConnected () {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client connected"));
+ #endif
+ client.println (F("220-Welcome to FTP for ESP8266/ESP32"));
+ client.println (F("220-By David Paiva"));
+ client.println (F("220-Version ") + String (FTP_SERVER_VERSION));
+ client.println (F("220 Put your ftp client in passive mode, and do not attempt more than one connection"));
+ iCL = 0;
+}
+
+void FtpServer::disconnectClient () {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> disconnecting client"));
+ #endif
+ abortTransfer ();
+ client.println (F("221 Goodbye"));
+ client.stop ();
+}
+
+boolean FtpServer::userIdentity () {
+ if (strcmp (command, "USER")) {
+ client.println (F("500 Syntax error"));
+ }
+ if (strcmp (parameters, _FTP_USER.c_str ())) {
+ client.println (F("530 user not found"));
+ }
+ else {
+ client.println (F("331 OK. Password required"));
+ strcpy (cwdName, "/");
+ return true;
+ }
+ millisDelay = millis () + 100; // delay of 100 ms
+ return false;
+}
+
+boolean FtpServer::userPassword () {
+ if (strcmp (command, "PASS")) {
+ client.println (F("500 Syntax error"));
+ }
+ else if (strcmp (parameters, _FTP_PASS.c_str ())) {
+ client.println (F("530 "));
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> user authenticated"));
+ #endif
+ client.println (F("230 OK."));
+ return true;
+ }
+ millisDelay = millis () + 100; // delay of 100 ms
+ return false;
+}
+
+boolean FtpServer::processCommand () {
+ struct tm * ptm;
+ time_t ftime;
+ char buffer[80];
+
+ ///////////////////////////////////////
+ // //
+ // ACCESS CONTROL COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // CDUP - Change to Parent Directory
+ //
+ if (!strcmp (command, "CDUP") || (!strcmp (command, "CWD") && !strcmp (parameters, ".."))) {
+ bool ok = false;
+ if (strlen (cwdName) > 1) { // do nothing if cwdName is root
+ // if cwdName ends with '/', remove it (must not append)
+ if (cwdName[strlen (cwdName) - 1] == '/') {
+ cwdName[ strlen (cwdName ) - 1 ] = 0;
+ }
+ // search last '/'
+ char * pSep = strrchr (cwdName, '/');
+ ok = pSep > cwdName;
+ // if found, ends the string on its position
+ if (ok) {
+ * pSep = 0;
+ ok = cfsp->exists (cwdName);
+ }
+ }
+ // if an error appends, move to root
+ if (!ok) {
+ strcpy (cwdName, "/");
+ }
+ client.println (F("250 Ok. Current directory is ") + String (cwdName));
+ }
+
+ //
+ // CWD - Change Working Directory
+ //
+ else if (!strcmp (command, "CWD")) {
+ char path[FTP_CWD_SIZE];
+ if (haveParameter () && makeExistsPath (path)) {
+ strcpy (cwdName, path);
+ client.println (F("250 Ok. Current directory is ") + String (cwdName));
+ }
+ }
+
+ //
+ // PWD - Print Directory
+ //
+ else if (!strcmp (command, "PWD")) {
+ client.println (F("257 \"") + String (cwdName) + F("\" is your current directory"));
+ }
+
+ //
+ // QUIT
+ //
+ else if (!strcmp (command, "QUIT")) {
+ disconnectClient ();
+ return false;
+ }
+
+ ///////////////////////////////////////
+ // //
+ // TRANSFER PARAMETER COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // MODE - Transfer Mode
+ //
+ else if (!strcmp (command, "MODE")) {
+ if (!strcmp (parameters, "S")) {
+ client.println (F("200 S Ok"));
+ }
+ else {
+ client.println (F("504 Only S (tream) is supported"));
+ }
+ }
+
+ //
+ // PASV - Passive Connection management
+ //
+ else if (!strcmp (command, "PASV")) {
+ if (data.connected ()) {
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ }
+ dataIp = WiFi.localIP ();
+ dataPort = FTP_DATA_PORT_PASV;
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> connection management set to passive"));
+ Serial.println (F("-> data port set to ") + String (dataPort));
+ #endif
+ client.println (F("227 Entering Passive Mode (") + String (dataIp[0]) + "," + String (dataIp[1]) + "," + String (dataIp[2]) + "," + String (dataIp[3]) + "," + String (dataPort >> 8) + "," + String (dataPort & 255) + ").");
+ dataPassiveConn = true;
+ }
+
+ //
+ // PORT - Data Port
+ //
+ else if (!strcmp (command, "PORT")) {
+ if (data) {
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ }
+ // get IP of data client
+ dataIp[0] = atoi (parameters);
+ char * p = strchr (parameters, ',');
+ for (uint8_t i = 1; i < 4; i ++) {
+ dataIp[i] = atoi (++ p);
+ p = strchr (p, ',');
+ }
+ // get port of data client
+ dataPort = 256 * atoi (++ p);
+ p = strchr (p, ',');
+ dataPort += atoi (++ p);
+ if (p == NULL) {
+ client.println (F("501 Can't interpret parameters"));
+ }
+ else {
+ client.println (F("200 PORT command successful"));
+ dataPassiveConn = false;
+ }
+ }
+
+ //
+ // STRU - File Structure
+ //
+ else if (!strcmp (command, "STRU")) {
+ if (!strcmp (parameters, "F")) {
+ client.println (F("200 F Ok"));
+ }
+ else {
+ client.println (F("504 Only F (ile) is supported"));
+ }
+ }
+
+ //
+ // TYPE - Data Type
+ //
+ else if (!strcmp (command, "TYPE")) {
+ if (!strcmp (parameters, "A")) {
+ client.println (F("200 TYPE is now ASCII"));
+ }
+ else if (!strcmp (parameters, "I" )) {
+ client.println (F("200 TYPE is now 8-bit binary"));
+ }
+ else {
+ client.println (F("504 Unknown TYPE"));
+ }
+ }
+
+ ///////////////////////////////////////
+ // //
+ // FTP SERVICE COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // ABOR - Abort
+ //
+ else if (!strcmp (command, "ABOR")) {
+ abortTransfer ();
+ client.println (F("226 Data connection closed"));
+ }
+
+ //
+ // DELE - Delete a File
+ //
+ else if (!strcmp (command, "DELE")) {
+ char path[FTP_CWD_SIZE];
+ if (strlen (parameters) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (path)) {
+ if (!cfsp->exists (path)) {
+ client.println (F("550 File ") + String (parameters) + F(" not found"));
+ }
+ else {
+ if (cfsp->remove (path)) {
+ client.println (F("250 Deleted ") + String (parameters));
+ // silently recreate the directory if it vanished with the last file it contained
+ String directory = String (path).substring (0, String(path).lastIndexOf ("/"));
+ if (!cfsp->exists (directory.c_str())) {
+ cfsp->mkdir (directory.c_str());
+ }
+ }
+ else {
+ client.println (F("450 Can't delete ") + String (parameters));
+ }
+ }
+ }
+ }
+
+ //
+ // LIST - List
+ //
+ else if (!strcmp (command, "LIST")) {
+ if (dataConnect ()) {
+ client.println (F("150 Accepted data connection"));
+ uint16_t nm = 0;
+
+ #ifdef ESP8266
+ Dir dir = cfsp->openDir (cwdName);
+ while (dir.next ()) {
+ String fname, fsize;
+ fname = dir.fileName ();
+ time_t ftime = dir.fileTime ();
+ ptm = gmtime (&ftime);
+ int pos = fname.lastIndexOf ("/"); //looking for the beginning of the file by the last "/"
+ fname.remove (0, pos + 1); //Delete everything up to and including the filename
+ fsize = String (dir.fileSize ());
+ if (dir.isDirectory ()){
+ sprintf (buffer, "%04d-%02d-%02d %02d:%02d %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, fname.c_str());
+ }
+ else {
+ sprintf (buffer, "%04d-%02d-%02d %02d:%02d %s %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, fillSpaces (14, String (fsize)).c_str(), fname.c_str());
+ }
+ data.println (buffer);
+ nm ++;
+ }
+ client.println( "226 " + String (nm) + " matches total");
+ #endif
+ #ifdef ESP32
+ File dir = cfsp->open (cwdName);
+ if ((!dir) || (!dir.isDirectory ())) {
+ client.println (F("550 Can't open directory ") + String (cwdName));
+ }
+ else {
+ File file = dir.openNextFile ();
+ while (file) {
+ String fname, fsize;
+ fname = file.name ();
+ ftime = file.getLastWrite ();
+ ptm = gmtime (&ftime);
+ int pos = fname.lastIndexOf ("/"); //looking for the beginning of the file by the last "/"
+ fname.remove (0, pos + 1); //Delete everything up to and including the filename
+ #ifdef FTP_DEBUG
+ Serial.println ("-> " + fname);
+ #endif
+ fsize = String (file.size ());
+ if (file.isDirectory ()){
+ sprintf (buffer, "%04u-%02u-%02u %02u:%02u %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, fname.c_str());
+ }
+ else {
+ sprintf (buffer, "%04u-%02u-%02u %02d:%02u %s %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, fillSpaces (14, String (fsize)).c_str(), fname.c_str());
+ }
+ data.println (buffer);
+ nm ++;
+ file = dir.openNextFile ();
+ }
+ client.println ("226 " + String (nm) + F(" matches total"));
+ }
+ #endif
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ }
+ else {
+ client.println (F("425 No data connection"));
+ data.stop ();
+ }
+ }
+
+ //
+ // MLSD - Listing for Machine Processing (see RFC 3659)
+ //
+ else if (!strcmp (command, "MLSD")) {
+ if (!dataConnect ()) {
+ client.println (F("425 No data connection MLSD"));
+ }
+ else {
+ client.println (F("150 Accepted data connection"));
+ uint16_t nm = 0;
+ #ifdef ESP8266
+ Dir dir = cfsp->openDir (cwdName);
+ char dtStr[15];
+ while (dir.next ()) {
+ String fn, fs;
+ fn = dir.fileName ();
+ int pos = fn.lastIndexOf ("/"); //looking for the beginning of the file by the last "/"
+ fn.remove (0, pos + 1); //Delete everything up to and including the filename
+ fs = String (dir.fileSize ());
+ ftime = dir.fileTime ();
+ ptm = gmtime (&ftime);
+ if (dir.isDirectory ()) {
+ sprintf (buffer, "Type=dir;Modify=%04u%02u%02u%02u%02u%02u; %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, fn.c_str());
+ }
+ else {
+ sprintf (buffer, "Type=file;Size=%s;Modify=%04u%02u%02u%02u%02u%02u; %s", fs.c_str(), ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, fn.c_str());
+ }
+ data.println (buffer);
+ nm ++;
+ }
+ client.println (F("226-options: -a -l"));
+ client.println("226 " + String(nm) + F(" matches total"));
+ #endif
+ #ifdef ESP32
+ File dir = cfsp->open (cwdName);
+ char dtStr[15];
+ if (!cfsp->exists (cwdName)) {
+ client.println (F("550 Can't open directory ") + String (parameters));
+ }
+ else {
+ File file = dir.openNextFile ();
+ while (file) {
+ String fn, fs;
+ fn = file.name ();
+ int pos = fn.lastIndexOf ("/"); // looking for the beginning of the file by the last "/"
+ fn.remove (0, pos + 1); // delete everything up to and including the filename
+ fs = String (file.size ());
+ ftime = file.getLastWrite ();
+ ptm = gmtime (&ftime);
+ if (file.isDirectory ()) {
+ sprintf (buffer, "Type=dir;Modify=%04u%02u%02u%02u%02u%02u; %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, fn.c_str());
+ }
+ else {
+ sprintf (buffer, "Type=file;Size=%s;Modify=%04u%02u%02u%02u%02u%02u; %s", fs.c_str(), ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, fn.c_str());
+ }
+ data.println (buffer);
+ nm ++;
+ file = dir.openNextFile ();
+ }
+ client.println (F("226-options: -a -l"));
+ client.println ("226 " + String (nm) + F(" matches total"));
+ }
+ #endif
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ }
+ }
+
+ //
+ // NLST - Name List
+ //
+ else if (!strcmp (command, "NLST" )) {
+ if (!dataConnect ()) {
+ client.println (F("425 No data connection"));
+ }
+ else {
+ client.println (F("150 Accepted data connection"));
+ uint16_t nm = 0;
+ #ifdef ESP8266
+ Dir dir = cfsp->openDir (cwdName);
+ if (!cfsp->exists (cwdName)) {
+ client.println (F("550 Can't open directory ") + String (parameters));
+ }
+ else {
+ while (dir.next ()) {
+ data.println (dir.fileName ());
+ nm ++;
+ }
+ client.println ("226 " + String(nm) + F(" matches total"));
+ }
+ #endif
+ #ifdef ESP32
+ File dir = cfsp->open (cwdName);
+ if (!cfsp->exists (cwdName)) {
+ client.println (F("550 Can't open directory ") + String (parameters));
+ }
+ else {
+ File file = dir.openNextFile ();
+ while (file) {
+ data.println (file.name ());
+ nm ++;
+ file = dir.openNextFile ();
+ }
+ client.println ("226 " + String (nm) + F(" matches total"));
+ }
+ #endif
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ }
+ }
+
+ //
+ // NOOP
+ //
+ else if (!strcmp (command, "NOOP")) {
+ client.println (F("200 Zzz..."));
+ }
+
+ //
+ // RETR - Retrieve
+ //
+ else if (!strcmp (command, "RETR")) {
+ char path[FTP_CWD_SIZE];
+ if (strlen (parameters) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (path)) {
+ file = cfsp->open (path, "r");
+ if (!file) {
+ client.println (F("550 File ") + String (parameters) + F(" not found"));
+ }
+ else if (!file) {
+ client.println (F("450 Can't open ") + String (parameters));
+ }
+ else if (!dataConnect ()) {
+ client.println (F("425 No data connection"));
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> sending ") + String (parameters));
+ #endif
+ client.println (F("150-Connected to port ") + String (dataPort));
+ client.println (F("150 ") + String (file.size ()) + F(" bytes to download"));
+ millisBeginTrans = millis ();
+ bytesTransferred = 0;
+ transferStatus = 1;
+ }
+ }
+ }
+
+ //
+ // STOR - Store
+ //
+ else if (!strcmp (command, "STOR")) {
+ char path[FTP_CWD_SIZE];
+ if (strlen (parameters) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (path)) {
+ file = cfsp->open (path, "w");
+ if (!file) {
+ client.println (F("451 Can't open/create ") + String (parameters));
+ }
+ else if (!dataConnect ()) {
+ client.println (F("425 No data connection"));
+ file.close ();
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> receiving ") + String (parameters));
+ #endif
+ client.println (F("150 Connected to port ") + String (dataPort));
+ millisBeginTrans = millis ();
+ bytesTransferred = 0;
+ transferStatus = 2;
+ }
+ }
+ }
+
+ //
+ // MKD - Make Directory
+ //
+
+ else if (!strcmp (command, "MKD")) {
+ char path[FTP_CWD_SIZE];
+ if (haveParameter () && makePath (path)) {
+ if (cfsp->exists (path)) {
+ client.println (F("521 Can't create \"") + String (parameters) + F("\", Directory exists"));
+ }
+ else {
+ if (cfsp->mkdir (path)) {
+ client.println (F("257 \"") + String (parameters) + F("\" created"));
+ }
+ else {
+ client.println (F("550 Can't create \"") + String (parameters) + "\"");
+ }
+ }
+ }
+ }
+
+ //
+ // RMD - Remove a Directory
+ //
+ else if (!strcmp (command, "RMD")) {
+ char path[FTP_CWD_SIZE];
+ if (haveParameter () && makePath (path)) {
+ if (cfsp->rmdir (path)) {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> deleting ") + String (parameters));
+ #endif
+ client.println ("250 \"" + String (parameters) + F("\" deleted"));
+ }
+ else {
+ if (cfsp->exists (path)) { // hack
+ client.println (F("550 Can't remove \"") + String (parameters) + F("\". Directory not empty?"));
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> deleting ") + String (parameters));
+ #endif
+ client.println ("250 \"" + String (parameters) + F("\" deleted"));
+ }
+ }
+ }
+ }
+
+ //
+ // RNFR - Rename From
+ //
+ else if (!strcmp (command, "RNFR")) {
+ buf[0] = 0;
+ if (strlen (parameters) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (buf)) {
+ if (!cfsp->exists (buf)) {
+ client.println (F("550 File ") + String (parameters) + F(" not found"));
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> renaming ") + String (buf));
+ #endif
+ client.println (F("350 RNFR accepted - file exists, ready for destination"));
+ rnfrCmd = true;
+ }
+ }
+ }
+
+ //
+ // RNTO - Rename To
+ //
+ else if (!strcmp (command, "RNTO")) {
+ char path[FTP_CWD_SIZE];
+ char dir[FTP_FIL_SIZE];
+ if (strlen (buf ) == 0 || ! rnfrCmd) {
+ client.println (F("503 Need RNFR before RNTO"));
+ }
+ else if (strlen (parameters ) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (path)) {
+ if (cfsp->exists (path)) {
+ client.println (F("553 ") + String (parameters) + F(" already exists"));
+ }
+ else {
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> renaming ") + String (buf) + " to " + String (path));
+ #endif
+ if (cfsp->rename (buf, path)) {
+ client.println (F("250 File successfully renamed or moved"));
+ }
+ else {
+ client.println (F("451 Rename/move failure"));
+ }
+ }
+ }
+ rnfrCmd = false;
+ }
+
+ ///////////////////////////////////////
+ // //
+ // EXTENSIONS COMMANDS (RFC 3659) //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // FEAT - New Features
+ //
+ else if (!strcmp (command, "FEAT")) {
+ client.println (F("211-Extensions supported:"));
+ client.println (F(" MLSD"));
+ client.println (F("211 End."));
+ }
+
+ //
+ // MDTM - File Modification Time (see RFC 3659)
+ //
+ else if (!strcmp (command, "MDTM")) {
+ client.println (F("550 Unable to retrieve time"));
+ }
+
+ //
+ // SIZE - Size of the file
+ //
+ else if (!strcmp (command, "SIZE")) {
+ char path[FTP_CWD_SIZE];
+ if (strlen (parameters) == 0) {
+ client.println (F("501 No file name"));
+ }
+ else if (makePath (path)) {
+ file = cfsp->open (path, "r");
+ if (!file) {
+ client.println (F("450 Can't open ") + String (parameters));
+ }
+ else {
+ client.println (F("213 ") + String (file.size ()));
+ file.close ();
+ }
+ }
+ }
+
+ //
+ // SITE - System command
+ //
+ else if (!strcmp (command, "SITE")) {
+ client.println (F("500 Unknown SITE command ") + String (parameters));
+ }
+
+ //
+ // Unrecognized commands ...
+ //
+ else {
+ client.println (F("500 Unknown command"));
+ }
+ return true;
+}
+
+boolean FtpServer::dataConnect () {
+ unsigned long startTime = millis ();
+ //wait 5 seconds for a data connection
+ if (!data.connected ()) {
+ while (!dataServer->hasClient () && millis () - startTime < 10000) {
+ yield ();
+ }
+ if (dataServer->hasClient ()) {
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ data = dataServer->available ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client connected to dataserver"));
+ #endif
+ }
+ }
+ return data.connected ();
+}
+
+boolean FtpServer::doRetrieve () {
+ if (data.connected ()) {
+ int16_t nb = file.readBytes (buf, FTP_BUF_SIZE);
+ if (nb > 0) {
+ data.write ((uint8_t*)buf, nb);
+ bytesTransferred += nb;
+ return true;
+ }
+ }
+ closeTransfer ();
+ return false;
+}
+
+boolean FtpServer::doStore () {
+ // Avoid blocking by never reading more bytes than are available
+ int navail = data.available();
+ if (navail > 0) {
+ // And be sure not to overflow the buffer
+ if (navail > FTP_BUF_SIZE) {
+ navail = FTP_BUF_SIZE;
+ }
+ int16_t nb = data.read((uint8_t *)buf, navail);
+ if (nb > 0) {
+ file.write((uint8_t *)buf, nb);
+ bytesTransferred += nb;
+ }
+ }
+ if (!data.connected() && (navail <= 0)) {
+ closeTransfer();
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+void FtpServer::closeTransfer () {
+ uint32_t deltaT = (int32_t) (millis () - millisBeginTrans);
+ if (deltaT > 0 && bytesTransferred > 0) {
+ client.println (F("226-File successfully transferred"));
+ client.println ("226 " + String (deltaT) + " ms, " + String (bytesTransferred / deltaT) + " kbytes/s");
+ }
+ else {
+ client.println (F("226 File successfully transferred"));
+ }
+ file.close ();
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> file successfully transferred"));
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+}
+
+void FtpServer::abortTransfer () {
+ if (transferStatus > 0) {
+ file.close ();
+ data.stop ();
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> client disconnected from dataserver"));
+ #endif
+ client.println (F("426 Transfer aborted"));
+ #ifdef FTP_DEBUG
+ Serial.println (F("-> transfer aborted"));
+ #endif
+ }
+ transferStatus = 0;
+}
+
+// Read a char from client connected to ftp server
+//
+// update cmdLine and command buffers, iCL and parameters pointers
+//
+// return:
+// -2 if buffer cmdLine is full
+// -1 if line not completed
+// 0 if empty line received
+// length of cmdLine (positive) if no empty line received
+
+int8_t FtpServer::readChar () {
+ int8_t rc = -1;
+
+ if (client.available ()) {
+ char c = client.read ();
+ #ifdef FTP_DEBUG
+ Serial.print (c);
+ #endif
+ if (c == '\\') {
+ c = '/';
+ }
+ if (c != '\r' ) {
+ if (c != '\n' ) {
+ if (iCL < FTP_CMD_SIZE) {
+ cmdLine[iCL ++] = c;
+ }
+ else {
+ rc = -2; // Line too long
+ }
+ }
+ else {
+ cmdLine[iCL] = 0;
+ command[0] = 0;
+ parameters = NULL;
+ // empty line?
+ if (iCL == 0) {
+ rc = 0;
+ }
+ else {
+ rc = iCL;
+ // search for space between command and parameters
+ parameters = strchr (cmdLine, ' ');
+ if (parameters != NULL) {
+ if (parameters - cmdLine > 4) {
+ rc = -2; // Syntax error
+ }
+ else {
+ strncpy (command, cmdLine, parameters - cmdLine);
+ command[parameters - cmdLine] = 0;
+
+ while (* (++ parameters) == ' ') {
+ ;
+ }
+ }
+ }
+ else if (strlen (cmdLine) > 4) {
+ rc = -2; // Syntax error.
+ }
+ else {
+ strcpy (command, cmdLine);
+ }
+ iCL = 0;
+ }
+ }
+ }
+ if (rc > 0) {
+ for (uint8_t i = 0; i < strlen (command); i ++) {
+ command[i] = toupper (command[i]);
+ }
+ }
+ if (rc == -2) {
+ iCL = 0;
+ client.println (F("500 Syntax error"));
+ }
+ }
+ return rc;
+}
+
+// Make complete path/name from cwdName and parameters
+//
+// 3 possible cases: parameters can be absolute path, relative path or only the name
+//
+// parameters:
+// fullName : where to store the path/name
+//
+// return:
+// true, if done
+
+boolean FtpServer::makePath (char * fullName) {
+ return makePath (fullName, parameters);
+}
+
+boolean FtpServer::makePath (char * fullName, char * param) {
+ if (param == NULL) {
+ param = parameters;
+ }
+ // Root or empty?
+ if (strcmp (param, "/") == 0 || strlen (param) == 0) {
+ strcpy (fullName, "/");
+ return true;
+ }
+ // If relative path, concatenate with current dir
+ if (param[0] != '/' ) {
+ strcpy (fullName, cwdName);
+ if (fullName[strlen (fullName) - 1] != '/') {
+ strncat (fullName, "/", FTP_CWD_SIZE);
+ }
+ strncat (fullName, param, FTP_CWD_SIZE);
+ }
+ else {
+ strcpy (fullName, param);
+ }
+ // If ends with '/', remove it
+ uint16_t strl = strlen (fullName) - 1;
+ if (fullName[strl] == '/' && strl > 1) {
+ fullName[strl] = 0;
+ }
+ if (strlen (fullName) < FTP_CWD_SIZE) {
+ return true;
+ }
+ client.println (F("500 Command line too long"));
+ return false;
+}
+
+// Calculate year, month, day, hour, minute and second
+// from first parameter sent by MDTM command (YYYYMMDDHHMMSS)
+//
+// parameters:
+// pyear, pmonth, pday, phour, pminute and psecond: pointer of
+// variables where to store data
+//
+// return:
+// 0 if parameter is not YYYYMMDDHHMMSS
+// length of parameter + space
+
+uint8_t FtpServer::getDateTime (uint16_t * pyear, uint8_t * pmonth, uint8_t * pday,
+ uint8_t * phour, uint8_t * pminute, uint8_t * psecond) {
+ char dt[15];
+
+ // Date/time are expressed as a 14 digits long string
+ // terminated by a space and followed by name of file
+ if (strlen (parameters ) < 15 || parameters[14] != ' ') {
+ return 0;
+ }
+ for (uint8_t i = 0; i < 14; i ++) {
+ if (!isdigit (parameters[i])) {
+ return 0;
+ }
+ }
+ strncpy (dt, parameters, 14);
+ dt[14] = 0;
+ * psecond = atoi (dt + 12);
+ dt[12] = 0;
+ * pminute = atoi (dt + 10);
+ dt[10] = 0;
+ * phour = atoi (dt + 8);
+ dt[8] = 0;
+ * pday = atoi (dt + 6);
+ dt[6] = 0;
+ * pmonth = atoi (dt + 4);
+ dt[4] = 0;
+ * pyear = atoi (dt);
+ return 15;
+}
+
+// Create string YYYYMMDDHHMMSS from date and time
+//
+// parameters:
+// date, time
+// tstr: where to store the string. Must be at least 15 characters long
+//
+// return:
+// pointer to tstr
+
+char * FtpServer::makeDateTimeStr (char * tstr, uint16_t date, uint16_t time) {
+ sprintf (tstr, "%04u%02u%02u%02u%02u%02u",
+ ((date & 0xFE00) >> 9) + 1980, (date & 0x01E0) >> 5, date & 0x001F,
+ (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) << 1);
+ return tstr;
+}
+
+bool FtpServer::haveParameter () {
+ if (parameters != NULL && strlen (parameters) > 0) {
+ return true;
+ }
+ client.println (F("501 No file name"));
+ return false;
+}
+
+bool FtpServer::makeExistsPath (char * path, char * param) {
+ if (!makePath (path, param)) {
+ return false;
+ }
+ if (cfsp->exists (path)) {
+ return true;
+ }
+ client.println (F("550 ") + String (path) + F(" not found."));
+ return false;
+}
+
+String FtpServer::fillSpaces (uint8_t length, String input) {
+ String output;
+ output = "";
+ while (output.length() < length - input.length()) {
+ output += " ";
+ }
+ output += input;
+ return (output);
+}
diff --git a/lib/lib_div/ESPFtpServer/ESPFtpServer.h b/lib/lib_div/ESPFtpServer/ESPFtpServer.h
new file mode 100755
index 000000000..84f380658
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/ESPFtpServer.h
@@ -0,0 +1,125 @@
+/*
+* FTP SERVER FOR ESP8266/ESP32
+ * based on FTP Server for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200)
+ * based on Jean-Michel Gallego's work
+ * modified to work with esp8266 SPIFFS by David Paiva (david@nailbuster.com)
+ * 2017: modified by @robo8080 (ported to ESP32 and SD)
+ * 2019: modified by @fa1ke5 (use SD card in SD_MMC mode (No SD lib, SD_MMC lib), and added fully fuctional passive mode ftp server)
+ * 2020: modified by @jmwislez (support generic FS, and re-introduced ESP8266)
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+/*******************************************************************************
+ ** **
+ ** DEFINITIONS FOR FTP SERVER **
+ ** **
+ *******************************************************************************/
+
+// Uncomment to print debugging info to console
+//#define FTP_DEBUG
+
+#ifndef FTP_SERVERESP_H
+#define FTP_SERVERESP_H
+
+#include
+#include
+#ifdef ESP32
+ #include
+#else
+ #include
+#endif
+
+#define FTP_SERVER_VERSION "jmwislez/ESP32FtpServer 0.1.0"
+
+#define FTP_CTRL_PORT 21 // Command port on wich server is listening
+#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode
+
+#define FTP_TIME_OUT 5 // Disconnect client after 5 minutes of inactivity
+
+#ifdef ESP32
+#define FTP_CMD_SIZE 255 + 8 // max size of a command
+#define FTP_CWD_SIZE 255 + 8 // max size of a directory name
+#define FTP_FIL_SIZE 255 // max size of a file name
+#define FTP_BUF_SIZE 4096 //512 // 700 KByte/s download in AP mode, direct connection.
+#endif
+
+#ifdef ESP8266
+#define FTP_CMD_SIZE 128 // max size of a command
+#define FTP_CWD_SIZE 128 // max size of a directory name
+#define FTP_FIL_SIZE 64 // max size of a file name
+#define FTP_BUF_SIZE 256 // 700 KByte/s download in AP mode, direct connection.
+#endif
+
+class FtpServer {
+ public:
+ void begin(String uname, String pword, FS *ufp);
+ void handleFTP (void);
+ ~FtpServer(void);
+ bool is_up = false;
+
+ private:
+ bool haveParameter ();
+ bool makeExistsPath (char * path, char * param = NULL);
+ void iniVariables ();
+ void clientConnected ();
+ void disconnectClient ();
+ boolean userIdentity ();
+ boolean userPassword ();
+ boolean processCommand ();
+ boolean dataConnect ();
+ boolean doRetrieve ();
+ boolean doStore ();
+ void closeTransfer ();
+ void abortTransfer ();
+ boolean makePath (char * fullname);
+ boolean makePath (char * fullName, char * param);
+ uint8_t getDateTime (uint16_t * pyear, uint8_t * pmonth, uint8_t * pday,
+ uint8_t * phour, uint8_t * pminute, uint8_t * second);
+ char * makeDateTimeStr (char * tstr, uint16_t date, uint16_t time);
+ int8_t readChar ();
+ String fillSpaces (uint8_t len, String input_str);
+
+ IPAddress dataIp; // IP address of client for data
+ WiFiClient client;
+ WiFiClient data;
+
+ WiFiServer *ftpServer;
+ WiFiServer *dataServer;
+
+ FS *cfsp;
+
+ File file;
+
+ boolean dataPassiveConn;
+ uint16_t dataPort;
+ char buf[FTP_BUF_SIZE]; // data buffer for transfers
+ char cmdLine[FTP_CMD_SIZE]; // where to store incoming char from client
+ char cwdName[FTP_CWD_SIZE]; // name of current directory
+ char command[5]; // command sent by client
+ boolean rnfrCmd; // previous command was RNFR
+ char * parameters; // point to begin of parameters sent by client
+ uint16_t iCL; // pointer to cmdLine next incoming char
+ int8_t cmdStatus, // status of ftp command connexion
+ transferStatus; // status of ftp data transfer
+ uint32_t millisTimeOut, // disconnect after 5 min of inactivity
+ millisDelay,
+ millisEndConnection, //
+ millisBeginTrans, // store time of beginning of a transaction
+ bytesTransferred; //
+ String _FTP_USER;
+ String _FTP_PASS;
+};
+
+#endif // FTP_SERVERESP_H
diff --git a/lib/lib_div/ESPFtpServer/LICENSE b/lib/lib_div/ESPFtpServer/LICENSE
new file mode 100644
index 000000000..65c5ca88a
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/lib/lib_div/ESPFtpServer/README.md b/lib/lib_div/ESPFtpServer/README.md
new file mode 100644
index 000000000..8976e20d2
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/README.md
@@ -0,0 +1,14 @@
+# ESPFtpServer
+
+The history of this code is as follows:
+
+* https://github.com/gallegojm/Arduino-Ftp-Server/tree/master/FtpServer: FTP server for Arduino Mega2560 and Due with ethernet module W5100, W5200 or W5500
+* https://github.com/nailbuster/esp8266FTPServer: ported to ESP8266 and SPIFFS file system
+* https://github.com/robo8080/ESP32_FTPServer_SD: ported to ESP32 and SD card file system
+* https://github.com/fa1ke5/ESP32_FTPServer_SD_MMC: use SD card in SD_MMC mode (No SD lib, SD_MMC lib). Added fully fuctional passive mode ftp server, browse dir, change dir, rename dir/file, delete dir/file, upload and download files, dirs.
+
+The current repository is forked from fa1ke5, and has the following changes:
+* use of any file system file system (like SPIFFS/LittleFS/SD_MMC)
+* codebase to work for both ESP8266 and ESP32
+* clean-up of code layout and English
+* addition of library description files
diff --git a/lib/lib_div/ESPFtpServer/examples/ESPFtpServer.ino b/lib/lib_div/ESPFtpServer/examples/ESPFtpServer.ino
new file mode 100644
index 000000000..7fcd1a08c
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/examples/ESPFtpServer.ino
@@ -0,0 +1,104 @@
+// Uncomment the file system to use for FTP server
+
+#define FS_LITTLEFS
+//#define FS_SPIFFS
+//#define FS_SD_MMC
+
+#ifdef ESP32
+#include
+#endif
+#ifdef ESP8266
+#include
+#endif
+#include
+#include
+#include "ESPFtpServer.h"
+
+#if defined(FS_LITTLEFS)
+#ifdef ESP32
+#include "LITTLEFS.h"
+#define FS_ID LITTLEFS
+#endif
+#ifdef ESP8266
+#include "LittleFS.h"
+#define FS_ID LittleFS
+#endif
+#define FS_NAME "LittleFS"
+#elif defined(FS_SPIFFS)
+#ifdef ESP32
+#include "SPIFFS.h"
+#endif
+#define FS_ID SPIFFS
+#define FS_NAME "SPIFFS"
+#elif defined(FS_SD_MMC)
+#include "SD_MMC.h"
+#define FS_ID SD_MMC
+#define FS_NAME "SD_MMC"
+#else
+#define FS_ID SD
+#define FS_NAME "UNDEF"
+#endif
+
+const char* ssid = "*********************";
+const char* password = "*********************";
+
+const char* ntpServer = "pool.ntp.org";
+const long gmtOffset_sec = 3600;
+const int daylightOffset_sec = 3600;
+struct tm timeinfo;
+
+FtpServer ftpSrv; //set #define FTP_DEBUG in ESP32FtpServer.h to see ftp verbose on serial
+
+#ifdef ESP8266
+bool getLocalTime (struct tm * info) {
+ time_t now;
+
+ time(&now);
+ localtime_r (&now, info);
+
+ if (info->tm_year > (2016 - 1900)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+#endif
+
+void setup (void) {
+ Serial.begin (115200);
+
+ WiFi.begin (ssid, password);
+ Serial.println ("");
+
+ // Wait for connection
+ while (WiFi.status () != WL_CONNECTED) {
+ delay (500);
+ Serial.print (".");
+ }
+ Serial.println ("");
+ Serial.print ("Connected to ");
+ Serial.println (ssid);
+ Serial.print ("IP address: ");
+ Serial.println (WiFi.localIP ());
+
+ configTime (gmtOffset_sec, daylightOffset_sec, ntpServer);
+ while (!getLocalTime (&timeinfo)) {
+ delay (500);
+ Serial.print (".");
+ }
+ Serial.printf ("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (timeinfo.tm_year) + 1900, (timeinfo.tm_mon) + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
+
+ //FS_ID.format ();
+ if (FS_ID.begin ()) {
+ Serial.println ("File system opened (" + String (FS_NAME) + ")");
+ ftpSrv.begin ("esp32", "esp32"); //username, password for ftp. set ports in ESPFtpServer.h (default 21, 50009 for PASV)
+ }
+ else {
+ Serial.println ("File system could not be opened; ftp server will not work");
+ }
+}
+
+void loop (void){
+ ftpSrv.handleFTP (FS_ID); //make sure in loop you call handleFTP()!
+}
diff --git a/lib/lib_div/ESPFtpServer/library.json b/lib/lib_div/ESPFtpServer/library.json
new file mode 100644
index 000000000..9c2bc1525
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/library.json
@@ -0,0 +1,21 @@
+{
+ "name": "ESPFtpServer",
+ "keywords": "ftp, littlefs, spiffs, esp32, esp8266",
+ "description": "ESPFtpServer implements a simple FTP server (passive mode, only one connection at a time) on ESP8266 and ESP32, for any file system (SPIFFS/LittleFS/SD_MMC).",
+ "homepage": "https://github.com/jmwislez/ESPFtpServer/",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jmwislez/ESPFtpServer.git"
+ },
+ "version": "0.1.0",
+ "authors": {
+ "name": "Jean-Marc Wislez",
+ "url": "https://github.com/jmwislez/"
+ },
+ "exclude": [
+ ".github",
+ "extras"
+ ],
+ "frameworks": "arduino",
+ "platforms": ["espressif8266", "espressif32"]
+}
diff --git a/lib/lib_div/ESPFtpServer/library.properties b/lib/lib_div/ESPFtpServer/library.properties
new file mode 100644
index 000000000..e6de7105f
--- /dev/null
+++ b/lib/lib_div/ESPFtpServer/library.properties
@@ -0,0 +1,12 @@
+name=ESPFtpServer
+version=0.1.0
+author=Jean-Marc Wislez
+maintainer=Jean-Marc Wislez
+sentence=A simple FTP server for LittleFS/SPIFFS on ESP8266/ESP32
+paragraph=ESPFtpServer implements a simple FTP server (passive mode, only one connection at a time) on ESP8266 and ESP32, for any file system (SPIFFS/LittleFS/SD_MMC).
+category=Network
+url=https://github.com/jmwislez/ESPFtpServer/
+architectures=esp8266,esp32
+repository=https://github.com/jmwislez/ESPFtpServer.git
+license=GPL
+architectures=*
diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h
index 7ed4dbab3..b2df33857 100755
--- a/tasmota/include/tasmota_types.h
+++ b/tasmota/include/tasmota_types.h
@@ -264,8 +264,7 @@ typedef union {
uint32_t spare21 : 1; // bit 21
uint32_t spare22 : 1; // bit 22
uint32_t spare23 : 1; // bit 23
- uint32_t spare24 : 1; // bit 24
- uint32_t spare25 : 1; // bit 25
+ uint32_t FTP_Mode : 2; // bit 24, 25
uint32_t tariff_forced : 2; // bit 26..27 (v12.4.0.2) - Energy forced tariff : 0=tariff change on time, 1|2=tariff forced
uint32_t sunrise_dawn_angle : 2; // bits 28/29 (v12.1.1.4) -
uint32_t temperature_set_res : 2; // bits 30/31 (v9.3.1.4) - (Tuya)
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
index 1a515bd47..f577d5904 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
@@ -48,6 +48,13 @@ ufs fs info
ufstype get filesytem type 0=none 1=SD 2=Flashfile
ufssize total size in kB
ufsfree free size in kB
+ufsdelete
+ufsrename
+ufsrun
+ufsServe
+ftp start stop ftp server: 0 = OFF, 1 = SDC, 2 = FlashFile
+
+
\*********************************************************************************************/
#define XDRV_50 50
@@ -547,6 +554,9 @@ const char kUFSCommands[] PROGMEM = "Ufs|" // Prefix
"|Type|Size|Free|Delete|Rename|Run"
#ifdef UFILESYS_STATIC_SERVING
"|Serve"
+#endif
+#ifdef USE_FTP
+ "|FTP"
#endif
;
@@ -554,7 +564,10 @@ void (* const kUFSCommand[])(void) PROGMEM = {
&UFSInfo, &UFSType, &UFSSize, &UFSFree, &UFSDelete, &UFSRename, &UFSRun
#ifdef UFILESYS_STATIC_SERVING
,&UFSServe
-#endif
+#endif
+#ifdef USE_FTP
+ ,&Switch_FTP
+#endif
};
void UFSInfo(void) {
@@ -1348,17 +1361,70 @@ void UfsEditorUpload(void) {
#endif // USE_WEBSERVER
+
+#ifdef USE_FTP
+#include
+FtpServer *ftpSrv;
+
+void FTP_Server(uint32_t mode) {
+ if (mode > 0) {
+ if (ftpSrv) {
+ delete ftpSrv;
+ }
+ ftpSrv = new FtpServer;
+ if (mode == 1) {
+ ftpSrv->begin(USER_FTP,PW_FTP, ufsp);
+ } else {
+ ftpSrv->begin(USER_FTP,PW_FTP, ffsp);
+ }
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: FTP Server started in mode: '%d'"), mode);
+ } else {
+ if (ftpSrv) {
+ delete ftpSrv;
+ ftpSrv = nullptr;
+ }
+ }
+}
+
+void Switch_FTP(void) {
+ if (XdrvMailbox.data_len > 0) {
+ if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) {
+ FTP_Server(XdrvMailbox.payload);
+ Settings->mbflag2.FTP_Mode = XdrvMailbox.payload;
+ }
+ }
+ ResponseCmndNumber(Settings->mbflag2.FTP_Mode);
+}
+#endif // USE_FTP
+
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
+
bool Xdrv50(uint32_t function) {
bool result = false;
switch (function) {
case FUNC_LOOP:
UfsExecuteCommandFileLoop();
+
+#ifdef USE_FTP
+ if (ftpSrv) {
+ ftpSrv->handleFTP();
+ }
+#endif
+
break;
+
+ case FUNC_NETWORK_UP:
+#ifdef USE_FTP
+ if (Settings->mbflag2.FTP_Mode && !ftpSrv) {
+ FTP_Server(Settings->mbflag2.FTP_Mode);
+ }
+#endif
+ break;
+
/*
// Moved to support_tasmota.ino for earlier init to be used by scripter
#ifdef USE_SDCARD