diff --git a/CHANGLELOG.md b/CHANGLELOG.md
index ca55f036..b7b5b06f 100644
--- a/CHANGLELOG.md
+++ b/CHANGLELOG.md
@@ -50,6 +50,9 @@ Update libraries to ArduinoJson 6.19.1, TFT_eSPI 2.4.25, LovyanGFX 0.4.11 and Ad
### Objects
- Add `antiburn` command to prevent static parts of the screen to create a *ghosting* effect in some LCDs or conditions
+## Services
+- Add SimpleFTPServer to easily upload and download files to the plate *(one simultanious connection only)*
+
### Devices
- Add Analog touch driver for Unoshield displays (thanks @wesleygas)
- Add Arduitouch MOD ESP32 with 2.4" or 2.8"
diff --git a/data/script.js b/data/script.js
index 9a8e4bea..0b00a6e0 100644
--- a/data/script.js
+++ b/data/script.js
@@ -1 +1 @@
-function aref(e){setTimeout(function(){ref("")},1e3*e)}function ref(e){var o=(new Date).getTime();return document.getElementById("bmp").src="?a="+e+"&q="+o,!1}function about(){document.getElementById("doc").innerHTML='
openHASP
Copyright© 2019-2021 Francis Van RoieMIT LicensePermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Based on the previous work of the following open source developers:
HASwitchPlate
Copyright© 2019 Allen Derusha allen @derusha.orgMIT License
LVGL
Copyright© 2021 LVGL KftMIT License
zi Font Engine
Copyright© 2020-2021 Francis Van RoieMIT License
TFT_eSPI Library
Copyright© 2020 Bodmer (https://github.com/Bodmer) All rights reserved.FreeBSD License
includes parts from the Adafruit_GFX libraryCopyright© 2012 Adafruit Industries. All rights reservedBSD License
ArduinoJson
Copyright© 2014-2021 Benoit BLANCHONMIT License
PubSubClient
Copyright© 2008-2015 Nicholas O'LearyMIT License
ArduinoLog
Copyright© 2017,2018 Thijs Elenbaas, MrRobot62, rahuldeo2047, NOX73, dhylands, Josha blemasle, mfalkviddMIT License
QR Code generator
Copyright© Project NayukiMIT License
AceButton
Copyright© 2018 Brian T. ParkMIT License
'}
\ No newline at end of file
+function aref(e){setTimeout(function(){ref("")},1e3*e)}function ref(e){var o=(new Date).getTime();return document.getElementById("bmp").src="?a="+e+"&q="+o,!1}function about(){document.getElementById("doc").innerHTML='openHASP
Copyright© 2019-2021 Francis Van RoieMIT LicensePermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Based on the previous work of the following open source developers:
HASwitchPlate
Copyright© 2019 Allen Derusha allen @derusha.orgMIT License
LVGL
Copyright© 2021 LVGL KftMIT License
zi Font Engine
Copyright© 2020-2021 Francis Van RoieMIT License
TFT_eSPI Library
Copyright© 2020 Bodmer (https://github.com/Bodmer) All rights reserved.FreeBSD License
includes parts from the Adafruit_GFX libraryCopyright© 2012 Adafruit Industries. All rights reservedBSD License
ArduinoJson
Copyright© 2014-2021 Benoit BLANCHONMIT License
PubSubClient
Copyright© 2008-2015 Nicholas O'LearyMIT License
ArduinoLog
Copyright© 2017,2018 Thijs Elenbaas, MrRobot62, rahuldeo2047, NOX73, dhylands, Josha blemasle, mfalkviddMIT License
QR Code generator
Copyright© Project NayukiMIT License
SimpleFTPServer
Copyright© 2017 Renzo Mischianti www.mischianti.org All right reserved.MIT License
AceButton
Copyright© 2018 Brian T. ParkMIT License
'}
\ No newline at end of file
diff --git a/data/script.js.gz b/data/script.js.gz
index 35c934fd..a093c05f 100644
Binary files a/data/script.js.gz and b/data/script.js.gz differ
diff --git a/include/hasp_conf.h b/include/hasp_conf.h
index ae928c75..9551c809 100644
--- a/include/hasp_conf.h
+++ b/include/hasp_conf.h
@@ -85,6 +85,10 @@
#define HASP_START_HTTP 1
#endif
+#ifndef HASP_START_FTP
+#define HASP_START_FTP 1
+#endif
+
#ifndef HASP_USE_MDNS
#define HASP_USE_MDNS (HASP_HAS_NETWORK)
#endif
@@ -93,10 +97,18 @@
#define HASP_USE_SYSLOG (HASP_HAS_NETWORK)
#endif
+#ifndef HASP_USE_FTP
+#define HASP_USE_FTP 0
+#endif
+
#ifndef HASP_USE_TELNET
#define HASP_USE_TELNET 0
#endif
+#ifndef HASP_START_FTP
+#define HASP_START_FTP 1
+#endif
+
#ifndef HASP_START_TELNET
#define HASP_START_TELNET 1
#endif
@@ -279,6 +291,10 @@ static WiFiSpiClass WiFi;
#include "sys/svc/hasp_console.h"
#endif
+#if HASP_USE_FTP > 0
+#include "sys/svc/hasp_ftp.h"
+#endif
+
#if HASP_USE_TELNET > 0
#include "sys/svc/hasp_telnet.h"
#endif
diff --git a/lib/SimpleFTPServer/.project b/lib/SimpleFTPServer/.project
new file mode 100644
index 00000000..aa361b16
--- /dev/null
+++ b/lib/SimpleFTPServer/.project
@@ -0,0 +1,11 @@
+
+
+ SimpleFTPServer
+
+
+
+
+
+
+
+
diff --git a/lib/SimpleFTPServer/FtpServer.cpp b/lib/SimpleFTPServer/FtpServer.cpp
new file mode 100644
index 00000000..9464fd2e
--- /dev/null
+++ b/lib/SimpleFTPServer/FtpServer.cpp
@@ -0,0 +1,2104 @@
+/*
+ * FtpServer Arduino, esp8266 and esp32 library for Ftp Server
+ * Derived form Jean-Michel Gallego version
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ *
+ * Use Ethernet library
+ *
+ * Commands implemented:
+ * USER, PASS, AUTH (AUTH only return 'not implemented' code)
+ * CDUP, CWD, PWD, QUIT, NOOP
+ * MODE, PASV, PORT, STRU, TYPE
+ * ABOR, DELE, LIST, NLST, MLST, MLSD
+ * APPE, RETR, STOR
+ * MKD, RMD
+ * RNTO, RNFR
+ * MDTM, MFMT
+ * FEAT, SIZE
+ * SITE FREE
+ *
+ * Tested with those clients:
+ * under Windows:
+ * FTP Rush
+ * Filezilla
+ * WinSCP
+ * NcFTP, ncftpget, ncftpput
+ * Firefox
+ * command line ftp.exe
+ * under Ubuntu:
+ * gFTP
+ * Filezilla
+ * NcFTP, ncftpget, ncftpput
+ * lftp
+ * ftp
+ * Firefox
+ * under Android:
+ * AndFTP
+ * FTP Express
+ * Firefox
+ * with a second Arduino and sketch of SurferTim at
+ * http://playground.arduino.cc/Code/FTP
+ *
+ */
+
+#include
+
+FtpServer::FtpServer(uint16_t _cmdPort, uint16_t _pasvPort) : ftpServer(_cmdPort), dataServer(_pasvPort)
+{
+ cmdPort = _cmdPort;
+ pasvPort = _pasvPort;
+
+ millisDelay = 0;
+ nbMatch = 0;
+ iCL = 0;
+
+ iniVariables();
+}
+
+void FtpServer::end()
+{
+ if(client.connected()) {
+ disconnectClient();
+ }
+
+ ftpServer.end();
+ dataServer.end();
+ DEBUG_PRINTLN(F("Stop server!"));
+
+ cmdStage = FTP_Init;
+}
+
+void FtpServer::begin(const char* _user, const char* _pass, const char* _welcomeMessage)
+{
+ // Tells the ftp server to begin listening for incoming connection
+ ftpServer.begin();
+#if defined(ESP8266) || FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN
+ ftpServer.setNoDelay(true);
+#endif
+ // localIp = _localIP == FTP_NULLIP() || (uint32_t) _localIP == 0 ? NET_CLASS.localIP() : _localIP ;
+ localIp =
+ NET_CLASS.localIP(); //_localIP == FTP_NULLIP() || (uint32_t) _localIP == 0 ? NET_CLASS.localIP() : _localIP ;
+ // strcpy( user, FTP_USER );
+ // strcpy( pass, FTP_PASS );
+ if(strlen(_user) > 0 && strlen(_user) < FTP_CRED_SIZE) {
+ // strcpy( user, _user );
+ this->user = _user;
+ } else {
+ // strcpy( user, FTP_USER );
+ this->user = FTP_USER;
+ }
+ if(strlen(_pass) > 0 && strlen(_pass) < FTP_CRED_SIZE) {
+ // strcpy( pass, _pass );
+ this->pass = _pass;
+ } else {
+ // strcpy( pass, FTP_PASS );
+ this->pass = FTP_PASS;
+ }
+
+ // strcpy(_welcomeMessage, welcomeMessage);
+
+ this->welcomeMessage = _welcomeMessage;
+
+ dataServer.begin();
+#if defined(ESP8266) || FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN
+ dataServer.setNoDelay(true);
+#endif
+
+ millisDelay = 0;
+ cmdStage = FTP_Stop;
+ iniVariables();
+}
+
+void FtpServer::credentials(const char* _user, const char* _pass)
+{
+ if(strlen(_user) > 0 && strlen(_user) < FTP_CRED_SIZE)
+ // strcpy( user, _user );
+ this->user = user;
+ if(strlen(_pass) > 0 && strlen(_pass) < FTP_CRED_SIZE)
+ // strcpy( pass, _pass );
+ this->pass = _pass;
+}
+
+void FtpServer::iniVariables()
+{
+ // Default for data port
+ dataPort = FTP_DATA_PORT_DFLT;
+
+ // Default Data connection is Active
+ dataConn = FTP_NoConn;
+
+ // Set the root directory
+ strcpy(cwdName, "/");
+
+ rnfrCmd = false;
+ transferStage = FTP_Close;
+}
+
+uint8_t FtpServer::handleFTP()
+{
+#ifdef FTP_ADDITIONAL_DEBUG
+ // int8_t data0 = data.status();
+ ftpTransfer transferStage0 = transferStage;
+ ftpCmd cmdStage0 = cmdStage;
+ ftpDataConn dataConn0 = dataConn;
+#endif
+
+ if((int32_t)(millisDelay - millis()) <= 0) {
+ if(cmdStage == FTP_Stop) {
+ if(client.connected()) {
+ DEBUG_PRINTLN(F("Disconnect client!"));
+ disconnectClient();
+ }
+ cmdStage = FTP_Init;
+ } else if(cmdStage == FTP_Init) { // Ftp server waiting for connection
+ abortTransfer();
+ iniVariables();
+ DEBUG_PRINT(F(" Ftp server waiting for connection on port "));
+ DEBUG_PRINTLN(cmdPort);
+
+ cmdStage = FTP_Client;
+ } else if(cmdStage == FTP_Client) { // Ftp server idle
+#ifdef ESP8266
+ if(ftpServer.hasClient()) {
+ client.stop();
+ client = ftpServer.available();
+ }
+#else
+ if(client && !client.connected()) {
+ client.stop();
+ DEBUG_PRINTLN(F("CLIENT STOP!!"));
+ }
+ client = ftpServer.accept();
+#endif
+ if(client.connected()) // A client connected
+ {
+ clientConnected();
+ millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait client id for 10 s.
+ cmdStage = FTP_User;
+ }
+ } else if(readChar() > 0) // got response
+ {
+ processCommand();
+ if(cmdStage == FTP_Stop)
+ millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait authentication for 10 s.
+ else if(cmdStage < FTP_Cmd)
+ millisDelay = millis() + 200; // delay of 100 ms
+ else
+ millisEndConnection = millis() + 1000L * FTP_TIME_OUT;
+ } else if(!client.connected()) {
+ cmdStage = FTP_Init;
+ }
+ if(transferStage == FTP_Retrieve) // Retrieve data
+ {
+ if(!doRetrieve()) {
+ transferStage = FTP_Close;
+ }
+ } else if(transferStage == FTP_Store) // Store data
+ {
+ if(!doStore()) {
+ if(FtpServer::_callback) {
+ FtpServer::_callback(FTP_FREE_SPACE_CHANGE, free(), capacity());
+ }
+
+ transferStage = FTP_Close;
+ }
+ } else if(transferStage == FTP_List || transferStage == FTP_Nlst) // LIST or NLST
+ {
+ if(!doList()) {
+ transferStage = FTP_Close;
+ }
+ } else if(transferStage == FTP_Mlsd) // MLSD listing
+ {
+ if(!doMlsd()) {
+
+ transferStage = FTP_Close;
+ }
+ } else if(cmdStage > FTP_Client && !((int32_t)(millisEndConnection - millis()) > 0)) {
+ client.println(F("530 Timeout"));
+ millisDelay = millis() + 200; // delay of 200 ms
+ cmdStage = FTP_Stop;
+ }
+
+#ifdef FTP_ADDITIONAL_DEBUG
+ if(cmdStage != cmdStage0 || transferStage != transferStage0 || dataConn != dataConn0) {
+ DEBUG_PRINT(F(" Command Old: "));
+ DEBUG_PRINT(cmdStage0);
+ DEBUG_PRINT(F(" Transfer Old: "));
+ DEBUG_PRINT(transferStage0);
+ DEBUG_PRINT(F(" Data Old: "));
+ DEBUG_PRINTLN(dataConn0);
+
+ DEBUG_PRINT(F(" Command : "));
+ DEBUG_PRINT(cmdStage);
+ DEBUG_PRINT(F(" Transfer : "));
+ DEBUG_PRINT(transferStage);
+ DEBUG_PRINT(F(" Data : "));
+ DEBUG_PRINTLN(dataConn);
+ }
+#endif
+ }
+ return cmdStage | (transferStage << 3) | (dataConn << 6);
+}
+
+void FtpServer::clientConnected()
+{
+ DEBUG_PRINTLN(F(" Client connected!"));
+ client.print(F("220---"));
+ client.print(welcomeMessage);
+ client.println(F(" ---"));
+ client.println(F("220--- By Renzo Mischianti ---"));
+ client.print(F("220 -- Version "));
+ client.print(FTP_SERVER_VERSION);
+ client.println(F(" --"));
+ iCL = 0;
+ if(FtpServer::_callback) {
+ FtpServer::_callback(FTP_CONNECT, free(), capacity());
+ }
+}
+
+void FtpServer::disconnectClient()
+{
+ DEBUG_PRINTLN(F(" Disconnecting client"));
+
+ abortTransfer();
+ client.println(F("221 Goodbye"));
+
+ if(FtpServer::_callback) {
+ FtpServer::_callback(FTP_DISCONNECT, free(), capacity());
+ }
+
+ if(client) {
+ }
+ if(data) {
+ data.stop();
+ }
+}
+
+bool FtpServer::processCommand()
+{
+ ///////////////////////////////////////
+ // //
+ // AUTHENTICATION COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ // RoSchmi added the next two lines
+ DEBUG_PRINT("Command is: ");
+ DEBUG_PRINTLN(command);
+
+ //
+ // USER - User Identity
+ //
+ if(CommandIs("USER")) {
+ if(!strcmp(parameter, user)) {
+ client.println(F("331 Ok. Password required"));
+ strcpy(cwdName, "/");
+ cmdStage = FTP_Pass;
+ } else {
+ client.println(F("530 "));
+ cmdStage = FTP_Stop;
+ }
+ }
+ //
+ // PASS - Password
+ //
+ else if(CommandIs("PASS")) {
+ if(cmdStage != FTP_Pass) {
+ client.println(F("503 "));
+ cmdStage = FTP_Stop;
+ }
+ if(!strcmp(parameter, pass)) {
+ DEBUG_PRINTLN(F(" Authentication Ok. Waiting for commands."));
+
+ client.println(F("230 Ok"));
+ cmdStage = FTP_Cmd;
+ } else {
+ client.println(F("530 "));
+ cmdStage = FTP_Stop;
+ }
+ }
+ //
+ // FEAT - New Features
+ //
+ else if(CommandIs("FEAT")) {
+ client.println(F("211-Extensions suported:"));
+ client.println(F(" MLST type*;modify*;size*;"));
+ client.println(F(" MLSD"));
+ client.println(F(" MDTM"));
+ client.println(F(" MFMT"));
+ client.println(F(" SIZE"));
+ client.println(F(" SITE FREE"));
+ client.println(F("211 End."));
+ }
+ //
+ // AUTH - Not implemented
+ //
+ else if(CommandIs("AUTH"))
+ client.println(F("502 "));
+ //
+ // Unrecognized commands at stage of authentication
+ //
+ else if(cmdStage < FTP_Cmd) {
+ client.println(F("530 "));
+ cmdStage = FTP_Stop;
+ }
+
+ ///////////////////////////////////////
+ // //
+ // ACCESS CONTROL COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // PWD - Print Directory
+ //
+ else if(CommandIs("PWD") || (CommandIs("CWD") && ParameterIs("."))) {
+ client.print(F("257 \""));
+ client.print(cwdName);
+ client.print(F("\""));
+ client.println(F(" is your current directory"));
+ //
+ // CDUP - Change to Parent Directory
+ //
+ } else if(CommandIs("CDUP") || (CommandIs("CWD") && ParameterIs(".."))) {
+ 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 = exists(cwdName);
+ }
+ }
+ // if an error appends, move to root
+ if(!ok) {
+ strcpy(cwdName, "/");
+ }
+ client.print(F("250 Ok. Current directory is "));
+ client.println(cwdName);
+ }
+ //
+ // CWD - Change Working Directory
+ //
+ else if(CommandIs("CWD")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makeExistsPath(path)) {
+ strcpy(cwdName, path);
+ client.print(F("250 Directory changed to "));
+ client.print(cwdName);
+ client.println();
+ }
+ }
+ //
+ // QUIT
+ //
+ else if(CommandIs("QUIT")) {
+ client.println(F("221 Goodbye"));
+ disconnectClient();
+ cmdStage = FTP_Stop;
+ }
+
+ ///////////////////////////////////////
+ // //
+ // TRANSFER PARAMETER COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // MODE - Transfer Mode
+ //
+ else if(CommandIs("MODE")) {
+ if(ParameterIs("S")) {
+ client.println(F("200 S Ok"));
+ } else {
+ client.println(F("504 Only S(tream) is suported"));
+ }
+ }
+ //
+ // PASV - Passive Connection management
+ //
+ else if(CommandIs("PASV")) {
+ data.stop();
+ dataServer.begin();
+ if((((uint32_t)NET_CLASS.localIP()) & ((uint32_t)NET_CLASS.subnetMask())) ==
+ (((uint32_t)client.remoteIP()) & ((uint32_t)NET_CLASS.subnetMask()))) {
+ dataIp = NET_CLASS.localIP();
+ } else {
+ dataIp = localIp;
+ }
+ dataPort = pasvPort;
+ DEBUG_PRINTLN(F(" Connection management set to passive"));
+ DEBUG_PRINT(F(" Listening at "));
+ DEBUG_PRINT(int(dataIp[0]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINT(int(dataIp[1]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINT(int(dataIp[2]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINT(int(dataIp[3]));
+ DEBUG_PRINT(F(":"));
+ DEBUG_PRINTLN(dataPort);
+
+ client.print(F("227 Entering Passive Mode"));
+ client.print(F(" ("));
+ client.print(int(dataIp[0]));
+ client.print(F(","));
+ client.print(int(dataIp[1]));
+ client.print(F(","));
+ client.print(int(dataIp[2]));
+ client.print(F(","));
+ client.print(int(dataIp[3]));
+ client.print(F(","));
+ client.print((dataPort >> 8));
+ client.print(F(","));
+ client.print((dataPort & 255));
+ client.println(F(")"));
+ dataConn = FTP_Pasive;
+ }
+ //
+ // PORT - Data Port
+ //
+ else if(CommandIs("PORT")) {
+ data.stop();
+ // get IP of data client
+ dataIp[0] = atoi(parameter);
+ char* p = strchr(parameter, ',');
+ 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 {
+ DEBUG_PRINT(F(" Data IP set to "));
+ DEBUG_PRINT(int(dataIp[0]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINT(int(dataIp[1]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINT(int(dataIp[2]));
+ DEBUG_PRINT(F("."));
+ DEBUG_PRINTLN(int(dataIp[3]));
+ DEBUG_PRINT(F(" Data port set to "));
+ DEBUG_PRINTLN(dataPort);
+
+ client.println(F("200 PORT command successful"));
+ dataConn = FTP_Active;
+ }
+ }
+ //
+ // STRU - File Structure
+ //
+ else if(CommandIs("STRU")) {
+ if(ParameterIs("F")) {
+ client.println(F("200 F Ok"));
+ // else if( ParameterIs( "R" ))
+ // client.println(F("200 B Ok") );
+ } else {
+ client.println(F("504 Only F(ile) is suported"));
+ }
+ }
+ //
+ // TYPE - Data Type
+ //
+ else if(CommandIs("TYPE")) {
+ if(ParameterIs("A")) {
+ client.println(F("200 TYPE is now ASCII"));
+ } else if(ParameterIs("I")) {
+ client.println(F("200 TYPE is now 8-bit binary"));
+ } else {
+ client.println(F("504 Unknow TYPE"));
+ }
+ }
+
+ ///////////////////////////////////////
+ // //
+ // FTP SERVICE COMMANDS //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // ABOR - Abort
+ //
+ else if(CommandIs("ABOR")) {
+ abortTransfer();
+ client.println(F("226 Data connection closed"));
+ }
+ //
+ // DELE - Delete a File
+ //
+ else if(CommandIs("DELE")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makeExistsPath(path)) {
+ if(remove(path)) {
+ if(FtpServer::_callback) {
+ FtpServer::_callback(FTP_FREE_SPACE_CHANGE, free(), capacity());
+ }
+
+ client.print(F("250 Deleted "));
+ client.println(parameter);
+ } else {
+ client.print(F("450 Can't delete "));
+ client.println(parameter);
+ }
+ }
+ }
+ //
+ // LIST - List
+ // NLST - Name List
+ // MLSD - Listing for Machine Processing (see RFC 3659)
+ //
+ else if(CommandIs("LIST") || CommandIs("NLST") || CommandIs("MLSD")) {
+ DEBUG_PRINT("List of file!!");
+
+ if(dataConnect()) {
+ if(openDir(&dir)) {
+ DEBUG_PRINT("Dir opened!!");
+
+ nbMatch = 0;
+ if(CommandIs("LIST"))
+ transferStage = FTP_List;
+ else if(CommandIs("NLST"))
+ transferStage = FTP_Nlst;
+ else
+ transferStage = FTP_Mlsd;
+ } else {
+ DEBUG_PRINT("List Data stop!!");
+ data.stop();
+ }
+ }
+ }
+ //
+ // MLST - Listing for Machine Processing (see RFC 3659)
+ //
+ else if(CommandIs("MLST")) {
+ char path[FTP_CWD_SIZE];
+ uint16_t dat, tim;
+ char dtStr[15];
+ bool isdir;
+ if(haveParameter() && makeExistsPath(path)) {
+ if(!getFileModTime(path, &dat, &tim)) {
+ client.print(F("550 Unable to retrieve time for "));
+ client.println(parameter);
+ } else {
+ isdir = isDir(path);
+ client.println(F("250-Begin"));
+ client.print(F(" Type="));
+ client.print((isdir ? F("dir") : F("file")));
+ client.print(F(";Modify="));
+ client.print(makeDateTimeStr(dtStr, dat, tim));
+ if(!isdir) {
+ if(openFile(path, FTP_FILE_READ)) {
+ client.print(F(";Size="));
+ client.print(long(fileSize(file)));
+ file.close();
+ }
+ }
+ client.print(F("; "));
+ client.println(path);
+ client.println(F("250 End."));
+ }
+ }
+ }
+ //
+ // NOOP
+ //
+ else if(CommandIs("NOOP")) {
+ client.println(F("200 Zzz..."));
+ }
+ //
+ // RETR - Retrieve
+ //
+ else if(CommandIs("RETR")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makeExistsPath(path)) {
+ if(!openFile(path, FTP_FILE_READ)) {
+ client.print(F("450 Can't open "));
+ client.print(parameter);
+ } else if(dataConnect(false)) {
+ DEBUG_PRINT(F(" Sending "));
+ DEBUG_PRINT(parameter);
+ DEBUG_PRINT(F(" size "));
+ DEBUG_PRINTLN(long(fileSize(file)));
+
+ if(FtpServer::_transferCallback) {
+ FtpServer::_transferCallback(FTP_DOWNLOAD_START, path, long(fileSize(file)));
+ }
+
+ client.print(F("150-Connected to port "));
+ client.println(dataPort);
+ client.print(F("150 "));
+ client.print(long(fileSize(file)));
+ client.println(F(" bytes to download"));
+ millisBeginTrans = millis();
+ bytesTransfered = 0;
+ transferStage = FTP_Retrieve;
+ }
+ }
+ }
+ //
+ // STOR - Store
+ // APPE - Append
+ //
+ else if(CommandIs("STOR") || CommandIs("APPE")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makePath(path)) {
+ bool open;
+ if(exists(path)) {
+ DEBUG_PRINTLN(F("APPEND FILE!!"));
+ open = openFile(path, (CommandIs("APPE") ? FTP_FILE_WRITE_APPEND : FTP_FILE_WRITE_CREATE));
+ } else {
+ DEBUG_PRINTLN(F("CREATE FILE!!"));
+ open = openFile(path, FTP_FILE_WRITE_CREATE);
+ }
+
+ data.stop();
+ data.flush();
+
+ DEBUG_PRINT(F("open/create "));
+ DEBUG_PRINTLN(open);
+ if(!open) {
+ client.print(F("451 Can't open/create "));
+ client.println(parameter);
+ } else if(!dataConnect()) // && !data.available())
+ file.close();
+ else {
+ DEBUG_PRINT(F(" Receiving "));
+ DEBUG_PRINTLN(parameter);
+
+ millisBeginTrans = millis();
+ bytesTransfered = 0;
+ transferStage = FTP_Store;
+
+ if(FtpServer::_transferCallback) {
+
+ // FtpServer::_transferCallback(FTP_UPLOAD_START, parameter, bytesTransfered);
+ FtpServer::_transferCallback(FTP_UPLOAD_START, path, bytesTransfered);
+ }
+ }
+ }
+ }
+ //
+ // MKD - Make Directory
+ //
+ else if(CommandIs("MKD")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makePath(path)) {
+ if(exists(path)) {
+ client.print(F("521 \""));
+ client.print(parameter);
+ client.println(F("\" directory already exists"));
+ } else {
+ DEBUG_PRINT(F(" Creating directory "));
+ DEBUG_PRINTLN(parameter);
+
+#if STORAGE_TYPE != STORAGE_SPIFFS
+ if(makeDir(path)) {
+ client.print(F("257 \""));
+ client.print(parameter);
+ client.print(F("\""));
+ client.println(F(" created"));
+ } else {
+#endif
+ client.print(F("550 Can't create \""));
+ client.print(parameter);
+ client.println(F("\""));
+#if STORAGE_TYPE != STORAGE_SPIFFS
+ }
+#endif
+ }
+ }
+ }
+ //
+ // RMD - Remove a Directory
+ //
+ else if(CommandIs("RMD")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makeExistsPath(path)) {
+ if(removeDir(path)) {
+ DEBUG_PRINT(F(" Deleting "));
+ DEBUG_PRINTLN(path);
+
+ client.print(F("250 \""));
+ client.print(parameter);
+ client.println(F("\" deleted"));
+ } else {
+ client.print(F("550 Can't remove \""));
+ client.print(parameter);
+ client.println(F("\". Directory not empty?"));
+ }
+ }
+ }
+ //
+ // RNFR - Rename From
+ //
+ else if(CommandIs("RNFR")) {
+ rnfrName[0] = 0;
+ if(haveParameter() && makeExistsPath(rnfrName)) {
+ DEBUG_PRINT(F(" Ready for renaming "));
+ DEBUG_PRINTLN(rnfrName);
+
+ client.println(F("350 RNFR accepted - file exists, ready for destination"));
+ rnfrCmd = true;
+ }
+ }
+ //
+ // RNTO - Rename To
+ //
+ else if(CommandIs("RNTO")) {
+ char path[FTP_CWD_SIZE];
+ char dirp[FTP_FIL_SIZE];
+ if(strlen(rnfrName) == 0 || !rnfrCmd) {
+ client.println(F("503 Need RNFR before RNTO"));
+ } else if(haveParameter() && makePath(path)) {
+ if(exists(path)) {
+ client.print(F("553 "));
+ client.print(parameter);
+ client.println(F(" already exists"));
+ } else {
+ strcpy(dirp, path);
+ char* psep = strrchr(dirp, '/');
+ bool fail = psep == NULL;
+ if(!fail) {
+ if(psep == dirp) psep++;
+ *psep = 0;
+ // fail = ! isDir( dirp );
+ // if( fail ) {
+ // client.print( F("550 \"") ); client.print( dirp ); client.println( F("\" is not
+ // directory") );
+ // } else
+ // {
+ DEBUG_PRINT(F(" Renaming "));
+ DEBUG_PRINT(rnfrName);
+ DEBUG_PRINT(F(" to "));
+ DEBUG_PRINTLN(path);
+
+ if(rename(rnfrName, path))
+ client.println(F("250 File successfully renamed or moved"));
+ else
+ fail = true;
+ // }
+ }
+ if(fail) client.println(F("451 Rename/move failure"));
+ }
+ }
+ rnfrCmd = false;
+ }
+ /*
+ //
+ // SYST - System
+ //
+ else if( CommandIs( "SYST" ))
+ FtpOutCli << F("215 MSDOS") << endl;
+ */
+
+ ///////////////////////////////////////
+ // //
+ // EXTENSIONS COMMANDS (RFC 3659) //
+ // //
+ ///////////////////////////////////////
+
+ //
+ // MDTM && MFMT - File Modification Time (see RFC 3659)
+ //
+ else if(CommandIs("MDTM") || CommandIs("MFMT")) {
+ if(haveParameter()) {
+ char path[FTP_CWD_SIZE];
+ char* fname = parameter;
+ uint16_t year;
+ uint8_t month, day, hour, minute, second, setTime;
+ char dt[15];
+ bool mdtm = CommandIs("MDTM");
+
+ setTime = getDateTime(dt, &year, &month, &day, &hour, &minute, &second);
+ // fname point to file name
+ fname += setTime;
+ if(strlen(fname) <= 0) {
+ client.println(F("501 No file name"));
+ } else if(makeExistsPath(path, fname)) {
+ if(setTime) // set file modification time
+ {
+ if(timeStamp(path, year, month, day, hour, minute, second)) {
+ client.print(F("213 "));
+ client.println(dt);
+ } else {
+ client.println(F("550 Unable to modify time"));
+ }
+ } else if(mdtm) // get file modification time
+ {
+ uint16_t dat, tim;
+ char dtStr[15];
+ if(getFileModTime(path, &dat, &tim)) {
+ client.print(F("213 "));
+ client.println(makeDateTimeStr(dtStr, dat, tim));
+ } else {
+ client.println("550 Unable to retrieve time");
+ }
+ }
+ }
+ }
+ }
+ //
+ // SIZE - Size of the file
+ //
+ else if(CommandIs("SIZE")) {
+ char path[FTP_CWD_SIZE];
+ if(haveParameter() && makeExistsPath(path)) {
+ if(!openFile(path, FTP_FILE_READ)) {
+ client.print(F("450 Can't open "));
+ client.println(parameter);
+ } else {
+ client.print(F("213 "));
+ client.println(long(fileSize(file)));
+ file.close();
+ }
+ }
+ }
+ //
+ // SITE - System command
+ //
+ else if(CommandIs("SITE")) {
+ if(ParameterIs("FREE")) {
+ uint32_t capa = capacity();
+ if((capa >> 10) < 1000) { // less than 1 Giga
+ client.print(F("200 "));
+ client.print(free());
+ client.print(F(" kB free of "));
+ client.print(capa);
+ client.println(F(" kB capacity"));
+ } else {
+ client.print(F("200 "));
+ client.print((free() >> 10));
+ client.print(F(" MB free of "));
+ client.print((capa >> 10));
+ client.println(F(" MB capacity"));
+ }
+ } else {
+ client.print(F("500 Unknow SITE command "));
+ client.println(parameter);
+ }
+ }
+ //
+ // Unrecognized commands ...
+ //
+ else
+ client.println(F("500 Unknow command"));
+ return true;
+}
+
+int FtpServer::dataConnect(bool out150)
+{
+ if(!data.connected()) {
+ if(dataConn == FTP_Pasive) {
+ uint16_t count = 1000; // wait up to a second
+ while(!data.connected() && count-- > 0) {
+#ifdef ESP8266
+ if(dataServer.hasClient()) {
+ data.stop();
+ data = dataServer.available();
+ }
+#else
+ data = dataServer.accept();
+#endif
+ delay(1);
+ }
+ } else if(dataConn == FTP_Active)
+ data.connect(dataIp, dataPort);
+ }
+
+ //#ifdef ESP8266
+ if(!(data.connected() || data.available())) {
+ //#else
+ // if( ! ( data.connected() )) {
+ //#endif
+ client.println(F("425 No data connection"));
+ } else if(out150) {
+ client.print(F("150 Accepted data connection to port "));
+ client.println(dataPort);
+ }
+ //#ifdef ESP8266
+ return data.connected() || data.available();
+ //#else
+ // return data.connected();
+ //#endif
+}
+
+bool FtpServer::dataConnected()
+{
+ if(data.connected()) return true;
+ data.stop();
+ client.println(F("426 Data connection closed. Transfer aborted"));
+ transferStage = FTP_Close;
+ return false;
+}
+
+bool FtpServer::openDir(FTP_DIR* pdir)
+{
+ bool openD;
+#if(STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP8266))
+ if(cwdName == 0) {
+ dir = STORAGE_MANAGER.openDir("/");
+ } else {
+ dir = STORAGE_MANAGER.openDir(cwdName);
+ }
+ openD = dir.rewind();
+
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#elif STORAGE_TYPE == STORAGE_SD
+ if(cwdName == 0) {
+ dir = STORAGE_MANAGER.open("/");
+ } else {
+ dir = STORAGE_MANAGER.open(cwdName);
+ }
+ openD = true;
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#elif STORAGE_TYPE == STORAGE_FFAT || (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32))
+ if(cwdName == 0) {
+ dir = STORAGE_MANAGER.open("/");
+ } else {
+ dir = STORAGE_MANAGER.open(cwdName);
+ }
+ openD = true;
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#elif STORAGE_TYPE == STORAGE_SEEED_SD
+ if(cwdName == 0) {
+ DEBUG_PRINT("cwdName forced -> ");
+ DEBUG_PRINTLN(cwdName);
+
+ FTP_DIR d = STORAGE_MANAGER.open("/");
+ dir = d;
+ } else {
+ DEBUG_PRINT("cwdName -> ");
+ DEBUG_PRINTLN(cwdName);
+
+ FTP_DIR d = STORAGE_MANAGER.open(cwdName);
+ dir = d;
+ }
+
+ openD = dir.isDirectory();
+
+ //#ifdef FTP_ADDITIONAL_DEBUG
+ // DEBUG_PRINT("Listing directory: ");
+ // DEBUG_PRINTLN(cwdName);
+ //
+ // if (!dir) {
+ // DEBUG_PRINTLN("Failed to open directory");
+ // }
+ // if (!dir.isDirectory()) {
+ // DEBUG_PRINTLN("Not a directory");
+ // }
+ //
+ // File file = dir.openNextFile();
+ // while (file) {
+ // if (file.isDirectory()) {
+ // DEBUG_PRINT(" DIR : ");
+ // DEBUG_PRINTLN(file.name());
+ // } else {
+ // DEBUG_PRINT(" FILE: ");
+ // DEBUG_PRINT(file.name());
+ // DEBUG_PRINT(" SIZE: ");
+ // DEBUG_PRINTLN(file.size());
+ // }
+ // file = dir.openNextFile();
+ // }
+ //
+ // dir.rewindDirectory();
+ //
+ //#endif
+
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#elif STORAGE_TYPE == STORAGE_SPIFFS
+ if(cwdName == 0 || strcmp(cwdName, "/") == 0) {
+ DEBUG_PRINT("DIRECTORY / EXIST ");
+#if ESP8266
+ dir = STORAGE_MANAGER.openDir("/");
+#else
+ dir = STORAGE_MANAGER.open("/");
+#endif
+ openD = true;
+
+ } else {
+ openD = false;
+ }
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#else
+ if(cwdName == 0) {
+ openD = pdir->open("/");
+ } else {
+ openD = pdir->open(cwdName);
+ }
+ if(!openD) {
+ client.print(F("550 Can't open directory "));
+ client.println(cwdName);
+ }
+#endif
+ return openD;
+}
+
+bool FtpServer::doRetrieve()
+{
+ if(!dataConnected()) {
+ file.close();
+ return false;
+ }
+ int16_t nb = file.read(buf, FTP_BUF_SIZE);
+ if(nb > 0) {
+ data.write(buf, nb);
+ DEBUG_PRINT(F("NB --> "));
+ DEBUG_PRINTLN(nb);
+ bytesTransfered += nb;
+
+ if(FtpServer::_transferCallback) {
+ FtpServer::_transferCallback(FTP_DOWNLOAD, getFileName(&file), bytesTransfered);
+ }
+
+// RoSchmi
+#if STORAGE_TYPE != STORAGE_SEEED_SD
+ return true;
+#endif
+ }
+ closeTransfer();
+ return false;
+}
+
+bool FtpServer::doStore()
+{
+ int16_t na = data.available();
+ if(na == 0) {
+ DEBUG_PRINTLN("NO DATA AVAILABLE!");
+#if FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN
+ data.stop();
+#endif
+ if(data.connected()) {
+ return true;
+ } else {
+ closeTransfer();
+ return false;
+ }
+ }
+
+ if(na > FTP_BUF_SIZE) {
+ na = FTP_BUF_SIZE;
+ }
+ int16_t nb = data.read((uint8_t*)buf, na);
+ int16_t rc = 0;
+ if(nb > 0) {
+ DEBUG_PRINT("NB -> ");
+ DEBUG_PRINTLN(nb);
+
+ rc = file.write(buf, nb);
+ DEBUG_PRINT("RC -> ");
+ DEBUG_PRINTLN(rc);
+ bytesTransfered += nb;
+
+ if(FtpServer::_transferCallback) {
+
+ FtpServer::_transferCallback(FTP_UPLOAD, getFileName(&file), bytesTransfered);
+ }
+ }
+ if(nb < 0 || rc == nb) {
+ return true;
+ }
+ client.println(F("552 Probably insufficient storage space"));
+ file.close();
+ data.stop();
+ return false;
+}
+
+bool FtpServer::doList()
+{
+ if(!dataConnected()) {
+#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD
+ dir.close();
+#endif
+ return false;
+ }
+#if STORAGE_TYPE == STORAGE_SPIFFS
+#if ESP8266
+ if(dir.next())
+#else
+ FTP_FILE fileDir = dir.openNextFile();
+ if(fileDir)
+#endif
+ {
+
+ data.print(F("+r,s"));
+#if ESP8266
+ data.print(long(dir.fileSize()));
+ data.print(F(",\t"));
+ data.println(dir.fileName());
+#else
+ data.print(long(fileDir.size()));
+ data.print(F(",\t"));
+ data.println(fileDir.name());
+#endif
+ nbMatch++;
+ return true;
+ }
+#elif STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT
+#if ESP8266
+ if(dir.next())
+#else
+#if STORAGE_TYPE == STORAGE_SEEED_SD
+ FTP_FILE fileDir = STORAGE_MANAGER.open(dir.name());
+ fileDir = dir.openNextFile();
+#else
+ FTP_FILE fileDir = dir.openNextFile();
+#endif
+ if(fileDir)
+#endif
+ {
+
+ if(dir.isDirectory()) {
+ data.print(F("+/,\t"));
+ DEBUG_PRINT(F("+/,\t"));
+ } else {
+ data.print(F("+r,s"));
+ DEBUG_PRINT(F("+r,s"));
+ }
+#if ESP8266
+ data.print(long(dir.fileSize()));
+ data.print(F(",\t"));
+ data.println(dir.fileName());
+#elif STORAGE_TYPE == STORAGE_SEEED_SD
+ String fn = fileDir.name();
+ fn.remove(0, strlen(dir.name()));
+ if(fn[0] == '/') {
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ }
+ long fz = fileDir.size();
+#else
+ data.print(long(fileDir.size()));
+ data.print(F(",\t"));
+ data.println(fileDir.name());
+
+ DEBUG_PRINT(long(fileDir.size()));
+ DEBUG_PRINT(F(",\t"));
+ DEBUG_PRINTLN(fileDir.name());
+
+#endif
+
+ nbMatch++;
+ return true;
+ }
+#elif STORAGE_TYPE == STORAGE_SD
+ FTP_FILE fileDir = dir.openNextFile();
+ if(fileDir) {
+
+ data.print(F("+r,s"));
+ data.print(long(fileDir.size()));
+ data.print(F(",\t"));
+ data.println(fileDir.name());
+
+ nbMatch++;
+ return true;
+ }
+ //
+ //#elif STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_SD
+ // #if ESP8266 && STORAGE_TYPE != STORAGE_SD
+ // if( dir.next())
+ // #else
+ // File fileDir = dir.openNextFile();
+ // if( fileDir )
+ // #endif
+ // {
+ //
+ // #if (STORAGE_TYPE == STORAGE_LITTLEFS)
+ // if( dir.isDirectory()) {
+ // data.print( F("+/,\t") );
+ // } else {
+ // #endif
+ // data.print( F("+r,s") );
+ // #if ESP8266 && STORAGE_TYPE != STORAGE_SD
+ // data.print( long( dir.fileSize()) );
+ // data.print( F(",\t") );
+ // data.println( dir.fileName() );
+ // #else
+ // data.print( long( fileDir.size()) );
+ // data.print( F(",\t") );
+ // data.println( fileDir.name() );
+ // #endif
+ // #if (STORAGE_TYPE == STORAGE_LITTLEFS)
+ // }
+ // #endif
+ //
+ // nbMatch ++;
+ // return true;
+ // }
+ //
+
+#elif STORAGE_TYPE == STORAGE_FATFS
+ if(dir.nextFile()) {
+ if(dir.isDir()) {
+ data.print(F("+/,\t"));
+ } else {
+ data.print(F("+r,s"));
+ data.print(long(dir.fileSize()));
+ data.print(F(",\t"));
+ }
+ data.println(dir.fileName());
+ nbMatch++;
+ return true;
+ }
+#else
+ if(file.openNext(&dir, FTP_FILE_READ_ONLY)) {
+ if(file.isDir()) {
+ data.print(F("+/,\t"));
+ } else {
+ data.print(F("+r,s"));
+ data.print(long(fileSize(file)));
+ data.print(F(",\t"));
+ }
+ file.printName(&data);
+ data.println();
+ file.close();
+ nbMatch++;
+ return true;
+ }
+#endif
+ client.print(F("226 "));
+ client.print(nbMatch);
+ client.println(F(" matches total"));
+#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD
+ dir.close();
+#endif
+ data.stop();
+ return false;
+}
+
+bool FtpServer::doMlsd()
+{
+ if(!dataConnected()) {
+#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD
+ dir.close();
+#endif
+ DEBUG_PRINTLN(F("Not connected!!"));
+ return false;
+ }
+ DEBUG_PRINTLN(F("Connected!!"));
+
+#if STORAGE_TYPE == STORAGE_SPIFFS
+ DEBUG_PRINTLN("DIR MLSD ");
+#if ESP8266
+ if(dir.next())
+#else
+ File fileDir = dir.openNextFile();
+ if(fileDir)
+#endif
+ {
+ DEBUG_PRINTLN("DIR NEXT ");
+ char dtStr[15];
+
+ struct tm* timeinfo;
+
+#if ESP8266
+ time_t time = dir.fileTime();
+#else
+ time_t time = fileDir.getLastWrite();
+#endif
+
+ timeinfo = localtime(&time);
+
+ // 2000 01 01 16 06 56
+
+ strftime(dtStr, 15, "%Y%m%d%H%M%S", timeinfo);
+
+#if ESP8266
+ String fn = dir.fileName();
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ long fz = dir.fileSize();
+#else
+ String fn = fileDir.name();
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ long fz = fileDir.size();
+#endif
+
+ data.print(F("Type="));
+
+ data.print(F("file"));
+ data.print(F(";Modify="));
+ data.print(dtStr); // data.print( makeDateTimeStr( dtStr, time, time) );
+ data.print(F(";Size="));
+ data.print(fz);
+ data.print(F("; "));
+ data.println(fn);
+
+ DEBUG_PRINT(F("Type="));
+ DEBUG_PRINT(F("file"));
+
+ DEBUG_PRINT(F(";Modify="));
+ DEBUG_PRINT(dtStr); // DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) );
+ DEBUG_PRINT(F(";Size="));
+ DEBUG_PRINT(fz);
+ DEBUG_PRINT(F("; "));
+ DEBUG_PRINTLN(fn);
+
+ nbMatch++;
+ return true;
+ }
+#elif STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT
+ DEBUG_PRINTLN("DIR MLSD ");
+#if ESP8266
+ if(dir.next())
+#else
+#if STORAGE_TYPE == STORAGE_SEEED_SD
+ File fileDir = STORAGE_MANAGER.open(dir.name());
+ fileDir = dir.openNextFile();
+#else
+ File fileDir = dir.openNextFile();
+#endif
+ DEBUG_PRINTLN(dir);
+ DEBUG_PRINTLN(fileDir);
+ if(fileDir)
+#endif
+ {
+ DEBUG_PRINTLN("DIR NEXT ");
+ char dtStr[15];
+
+#if STORAGE_TYPE == STORAGE_SEEED_SD
+ struct tm* timeinfo;
+
+ strcpy(dtStr, "19700101000000");
+#else
+ struct tm* timeinfo;
+
+#if ESP8266
+ time_t time = dir.fileTime();
+#else
+ time_t time = fileDir.getLastWrite();
+#endif
+
+ timeinfo = localtime(&time);
+
+ // 2000 01 01 16 06 56
+
+ strftime(dtStr, 15, "%Y%m%d%H%M%S", timeinfo);
+#endif
+
+#if ESP8266
+ String fn = dir.fileName();
+ long fz = dir.fileSize();
+ FTP_DIR fileDir = dir;
+#elif STORAGE_TYPE == STORAGE_SEEED_SD
+ String fn = fileDir.name();
+ fn.remove(0, strlen(dir.name()));
+ if(fn[0] == '/') {
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ }
+ long fz = fileDir.size();
+#else
+ String fn = fileDir.name();
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ long fz = fileDir.size();
+#endif
+
+ data.print(F("Type="));
+
+ data.print((fileDir.isDirectory() ? F("dir") : F("file")));
+ data.print(F(";Modify="));
+ data.print(dtStr); // data.print( makeDateTimeStr( dtStr, time, time) );
+ data.print(F(";Size="));
+ data.print(fz);
+ data.print(F("; "));
+ data.println(fn);
+
+ DEBUG_PRINT(F("Type="));
+ DEBUG_PRINT((fileDir.isDirectory() ? F("dir") : F("file")));
+
+ DEBUG_PRINT(F(";Modify="));
+ DEBUG_PRINT(dtStr); // DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) );
+ DEBUG_PRINT(F(";Size="));
+ DEBUG_PRINT(fz);
+ DEBUG_PRINT(F("; "));
+ DEBUG_PRINTLN(fn);
+
+ nbMatch++;
+// RoSchmi: next line was commented
+#if STORAGE_TYPE == STORAGE_SEEED_SD
+ fileDir.close();
+#endif
+ return true;
+ }
+
+#elif STORAGE_TYPE == STORAGE_SD
+ DEBUG_PRINTLN("DIR MLSD ");
+ File fileDir = dir.openNextFile();
+ if(fileDir) {
+ DEBUG_PRINTLN("DIR NEXT ");
+ char dtStr[15];
+
+ struct tm* timeinfo;
+
+ strcpy(dtStr, "19700101000000");
+
+ // long fz = dir.fileSize();
+ String fn = fileDir.name();
+
+ //#ifdef ESP32
+ fn.remove(0, fn.lastIndexOf("/") + 1);
+ //#else if !defined(ESP8266)
+ // fn.remove(0, 1);
+ //#endif
+
+ long fz = fileDir.size();
+
+ data.print(F("Type="));
+
+ data.print((fileDir.isDirectory() ? F("dir") : F("file")));
+ data.print(F(";Modify="));
+ data.print(dtStr); // data.print( makeDateTimeStr( dtStr, time, time) );
+ data.print(F(";Size="));
+ data.print(fz);
+ data.print(F("; "));
+ data.println(fn);
+
+ DEBUG_PRINT(F("Type="));
+ DEBUG_PRINT((fileDir.isDirectory() ? F("dir") : F("file")));
+
+ DEBUG_PRINT(F(";Modify="));
+ DEBUG_PRINT(dtStr); // DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) );
+ DEBUG_PRINT(F(";Size="));
+ DEBUG_PRINT(fz);
+ DEBUG_PRINT(F("; "));
+ DEBUG_PRINTLN(fn);
+
+ nbMatch++;
+ return true;
+ }
+
+#elif STORAGE_TYPE == STORAGE_FATFS
+ if(dir.nextFile()) {
+ char dtStr[15];
+ data.print(F("Type="));
+ data.print((dir.isDir() ? F("dir") : F("file")));
+ data.print(F(";Modify="));
+ data.print(makeDateTimeStr(dtStr, dir.fileModDate(), dir.fileModTime()));
+ data.print(F(";Size="));
+ data.print(long(dir.fileSize()));
+ data.print(F("; "));
+ data.println(dir.fileName());
+ nbMatch++;
+ return true;
+ }
+#else
+ if(file.openNext(&dir, FTP_FILE_READ_ONLY)) {
+ char dtStr[15];
+ uint16_t filelwd, filelwt;
+ bool gfmt = getFileModTime(&filelwd, &filelwt);
+ DEBUG_PRINT("gfmt --> ");
+ DEBUG_PRINTLN(gfmt);
+ if(gfmt) {
+ data.print(F("Type="));
+ data.print((file.isDir() ? F("dir") : F("file")));
+ data.print(F(";Modify="));
+ data.print(makeDateTimeStr(dtStr, filelwd, filelwt));
+ data.print(F(";Size="));
+ data.print(long(fileSize(file)));
+ data.print(F("; "));
+ file.printName(&data);
+ data.println();
+
+ DEBUG_PRINT(F("Type="));
+ DEBUG_PRINT((file.isDir() ? F("dir") : F("file")));
+ DEBUG_PRINT(F(";Modify="));
+ DEBUG_PRINT(makeDateTimeStr(dtStr, filelwd, filelwt));
+ DEBUG_PRINT(F(";Size="));
+ DEBUG_PRINT(long(fileSize(file)));
+ DEBUG_PRINT(F("; "));
+ // DEBUG_PRINT(file.name());
+ DEBUG_PRINTLN();
+ nbMatch++;
+ }
+ file.close();
+ return gfmt;
+ }
+#endif
+ client.println(F("226-options: -a -l"));
+ client.print(F("226 "));
+ client.print(nbMatch);
+ client.println(F(" matches total"));
+#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD && \
+ STORAGE_TYPE != STORAGE_SEEED_SD
+ dir.close();
+#endif
+ data.stop();
+ DEBUG_PRINTLN(F("All file readed!!"));
+ return false;
+}
+
+void FtpServer::closeTransfer()
+{
+ uint32_t deltaT = (int32_t)(millis() - millisBeginTrans);
+ if(deltaT > 0 && bytesTransfered > 0) {
+ DEBUG_PRINT(F(" Transfer completed in "));
+ DEBUG_PRINT(deltaT);
+ DEBUG_PRINTLN(F(" ms, "));
+ DEBUG_PRINT(bytesTransfered / deltaT);
+ DEBUG_PRINTLN(F(" kbytes/s"));
+
+ if(FtpServer::_transferCallback) {
+ FtpServer::_transferCallback(FTP_TRANSFER_STOP, getFileName(&file), bytesTransfered);
+ }
+
+ client.println(F("226-File successfully transferred"));
+ client.print(F("226 "));
+ client.print(deltaT);
+ client.print(F(" ms, "));
+ client.print(bytesTransfered / deltaT);
+ client.println(F(" kbytes/s"));
+ } else
+ client.println(F("226 File successfully transferred"));
+
+ file.close();
+ data.stop();
+}
+
+void FtpServer::abortTransfer()
+{
+ if(transferStage != FTP_Close) {
+ if(FtpServer::_transferCallback) {
+ FtpServer::_transferCallback(FTP_TRANSFER_ERROR, getFileName(&file), bytesTransfered);
+ }
+
+ file.close();
+#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD
+ dir.close();
+#endif
+ client.println(F("426 Transfer aborted"));
+ DEBUG_PRINTLN(F(" Transfer aborted!"));
+
+ transferStage = FTP_Close;
+ }
+ // if( data.connected())
+ data.stop();
+}
+
+// Read a char from client connected to ftp server
+//
+// update cmdLine and command buffers, iCL and parameter 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();
+ DEBUG_PRINT("-");
+ DEBUG_PRINT(c);
+
+ 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;
+ parameter = NULL;
+ // empty line?
+ if(iCL == 0)
+ rc = 0;
+ else {
+ rc = iCL;
+ // search for space between command and parameter
+ parameter = strchr(cmdLine, ' ');
+ if(parameter != NULL) {
+ if(parameter - cmdLine > 4)
+ rc = -2; // Syntax error
+ else {
+ strncpy(command, cmdLine, parameter - cmdLine);
+ command[parameter - cmdLine] = 0;
+ while(*(++parameter) == ' ')
+ ;
+ }
+ } 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;
+}
+
+bool FtpServer::haveParameter()
+{
+ if(parameter != NULL && strlen(parameter) > 0) return true;
+ client.println("501 No file name");
+ return false;
+}
+
+// Make complete path/name from cwdName and param
+//
+// 3 possible cases: param can be absolute path, relative path or only the name
+//
+// parameter:
+// fullName : where to store the path/name
+//
+// return:
+// true, if done
+
+bool FtpServer::makePath(char* fullName, char* param)
+{
+ if(param == NULL) param = parameter;
+
+ // 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) {
+ client.println(F("500 Command line too long"));
+ return false;
+ }
+ for(uint8_t i = 0; i < strlen(fullName); i++)
+ if(!legalChar(fullName[i])) {
+ client.println(F("553 File name not allowed"));
+ return false;
+ }
+ return true;
+}
+
+bool FtpServer::makeExistsPath(char* path, char* param)
+{
+ if(!makePath(path, param)) return false;
+ // RoSchmi
+ //#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_SD
+#if(STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SEEED_SD)
+ if(strcmp(path, "/") == 0) return true;
+#endif
+ DEBUG_PRINT("PATH --> ")
+ DEBUG_PRINT(path)
+ if(exists(path)) {
+ DEBUG_PRINTLN(" ...EXIST!")
+ return true;
+ }
+ DEBUG_PRINTLN(" ...NOT EXIST!")
+ client.print(F("550 "));
+ client.print(path);
+ client.println(F(" not found."));
+ return false;
+}
+
+// Calculate year, month, day, hour, minute and second
+// from first parameter sent by MDTM command (YYYYMMDDHHMMSS)
+// Accept longer parameter YYYYMMDDHHMMSSmmm where mmm are milliseconds
+// but don't take in account additional digits
+//
+// parameters:
+// dt: 15 length string for 14 digits and terminator
+// 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
+//
+// Date/time are expressed as a 14 digits long string
+// terminated by a space and followed by name of file
+
+uint8_t FtpServer::getDateTime(char* dt, uint16_t* pyear, uint8_t* pmonth, uint8_t* pday, uint8_t* phour,
+ uint8_t* pminute, uint8_t* psecond)
+{
+ uint8_t i;
+ dt[0] = 0;
+ if(strlen(parameter) < 15) //|| parameter[ 14 ] != ' ' )
+ return 0;
+ for(i = 0; i < 14; i++)
+ if(!isdigit(parameter[i])) return 0;
+ for(i = 14; i < 18; i++)
+ if(parameter[i] == ' ')
+ break;
+ else if(!isdigit(parameter[i]))
+ return 0;
+ if(i == 18) return 0;
+ i++;
+
+ strncpy(dt, parameter, 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);
+ strncpy(dt, parameter, 14);
+ DEBUG_PRINT(F(" Modification time: "));
+ DEBUG_PRINT(*pyear);
+ DEBUG_PRINT(F("/"));
+ DEBUG_PRINT(int(*pmonth));
+ DEBUG_PRINT(F("/"));
+ DEBUG_PRINT(int(*pday));
+ DEBUG_PRINT(F(" "));
+ DEBUG_PRINT(int(*phour));
+ DEBUG_PRINT(F(":"));
+ DEBUG_PRINT(int(*pminute));
+ DEBUG_PRINT(F(":"));
+ DEBUG_PRINT(int(*psecond));
+ DEBUG_PRINT(F(" of file: "));
+ DEBUG_PRINTLN((char*)(parameter + i));
+
+ return i;
+}
+
+// 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;
+}
+
+uint16_t FtpServer::fileSize(FTP_FILE file)
+{
+#if(STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT || \
+ STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SEEED_SD)
+ return file.size();
+#else
+ return file.fileSize();
+#endif
+}
+
+#if(STORAGE_TYPE == STORAGE_SEEED_SD)
+bool FtpServer::openFile(char path[FTP_CWD_SIZE], int readTypeInt)
+{
+ DEBUG_PRINT(F("File to open "));
+ DEBUG_PRINT(path);
+ DEBUG_PRINT(F(" readType "));
+ DEBUG_PRINTLN(readTypeInt);
+
+ if(readTypeInt == 0X01) {
+ readTypeInt = FILE_READ;
+ } else {
+ readTypeInt = FILE_WRITE;
+ }
+
+ file = STORAGE_MANAGER.open(path, readTypeInt);
+ if(!file) { // && readTypeInt[0]==FILE_READ) {
+ return false;
+ } else {
+ DEBUG_PRINTLN("TRUE");
+
+ return true;
+ }
+}
+#elif(STORAGE_TYPE == STORAGE_SD && defined(ESP8266)) // FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266_242)
+bool FtpServer::openFile(char path[FTP_CWD_SIZE], int readTypeInt)
+{
+ DEBUG_PRINT(F("File to open "));
+ DEBUG_PRINT(path);
+ DEBUG_PRINT(F(" readType "));
+ DEBUG_PRINTLN(readTypeInt);
+
+ if(readTypeInt == 0X01) {
+ readTypeInt = FILE_READ;
+ } else {
+ readTypeInt = FILE_WRITE;
+ }
+
+ file = STORAGE_MANAGER.open(path, readTypeInt);
+ if(!file) { // && readTypeInt[0]==FILE_READ) {
+ return false;
+ } else {
+ DEBUG_PRINTLN("TRUE");
+
+ return true;
+ }
+}
+#elif(STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT)
+bool FtpServer::openFile(const char* path, const char* readType)
+{
+ DEBUG_PRINT(F("File to open "));
+ DEBUG_PRINT(path);
+ DEBUG_PRINT(F(" readType "));
+ DEBUG_PRINTLN(readType);
+ file = STORAGE_MANAGER.open(path, readType);
+ if(!file && readType[0] == 'r') {
+ return false;
+ } else {
+ DEBUG_PRINTLN("TRUE");
+
+ return true;
+ }
+}
+#elif STORAGE_TYPE <= STORAGE_SDFAT2
+bool FtpServer::openFile(char path[FTP_CWD_SIZE], int readTypeInt)
+{
+ DEBUG_PRINT(F("File to open "));
+ DEBUG_PRINT(path);
+ DEBUG_PRINT(F(" readType "));
+ DEBUG_PRINTLN(readTypeInt);
+
+ file = STORAGE_MANAGER.open(path, readTypeInt);
+ if(!file) {
+ return false;
+ } else {
+ DEBUG_PRINTLN("TRUE");
+
+ return true;
+ }
+}
+
+#else
+bool FtpServer::openFile(char path[FTP_CWD_SIZE], const char* readType)
+{
+ return openFile((const char*)path, readType);
+}
+bool FtpServer::openFile(const char* path, const char* readType)
+{
+ DEBUG_PRINT(F("File to open "));
+ DEBUG_PRINT(path);
+ DEBUG_PRINT(F(" readType "));
+ DEBUG_PRINTLN(readType);
+#if(STORAGE_TYPE == STORAGE_SD && !defined(ESP32))
+ if(readType == 0X01) {
+ readType = FILE_READ;
+ } else {
+ readType = FILE_WRITE;
+ }
+#endif
+ file = STORAGE_MANAGER.open(path, readType);
+ if(!file && readType[0] == 'r') {
+ return false;
+ } else {
+ DEBUG_PRINTLN("TRUE");
+
+ return true;
+ }
+}
+#endif
+
+// Return true if path points to a directory
+bool FtpServer::isDir(char* path)
+{
+#if(STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP8266))
+ FTP_DIR dir;
+ bool res;
+ dir = STORAGE_MANAGER.openDir(path);
+
+ return true;
+ // res = dir.isDirectory();
+ // return res;
+
+#elif STORAGE_TYPE == STORAGE_SPIFFS
+ if(strcmp(path, "/") == 0) {
+ return true;
+ }
+ return false; // no directory support
+#elif STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT || \
+ (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32))
+ FTP_DIR dir;
+ bool res;
+ dir = STORAGE_MANAGER.open(path);
+
+ // return true;
+ res = dir.isDirectory();
+ return res;
+#elif STORAGE_TYPE == STORAGE_FATFS
+ return STORAGE_MANAGER.isDir(path);
+#elif STORAGE_TYPE == STORAGE_SDFAT1 || STORAGE_TYPE == STORAGE_SDFAT2
+ // bool res = (!dir.open(path, FTP_FILE_READ) || !dir.isDir());
+ // dir.close();
+ // return res;
+ if(strcmp(path, "/") == 0) {
+ return true;
+ }
+ if(!openFile(path, FTP_FILE_READ)) {
+ return false;
+ }
+ return true;
+#else
+ FTP_FILE file;
+ bool res;
+
+ if(!openFile(path, FTP_FILE_READ)) {
+ return false;
+ }
+#if STORAGE_TYPE == STORAGE_SD
+ // if (strcmp(path, "/") == 0) return true;
+ // res = file.isDirectory();
+ // DEBUG_PRINT(path);
+ // DEBUG_PRINT(" IS DIRECOTORY --> ");
+ // DEBUG_PRINTLN(res);
+ return true;
+#else
+// res = file.isDir();
+// DEBUG_PRINT("IS DIRECTORY --> " );
+// DEBUG_PRINTLN(res);
+#endif
+ file.close();
+ return res;
+#endif
+}
+
+bool FtpServer::timeStamp(char* path, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
+ uint8_t second)
+{
+#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT || \
+ STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SEEED_SD
+ // struct tm tmDate = { second, minute, hour, day, month, year };
+ // time_t rawtime = mktime(&tmDate);
+
+ return true;
+ // setTime(rawtime);
+ // SPIFFS USE time() call
+// return STORAGE_MANAGER.timeStamp( path, year, month, day, hour, minute, second );
+#elif STORAGE_TYPE == STORAGE_FATFS
+ return STORAGE_MANAGER.timeStamp(path, year, month, day, hour, minute, second);
+#else
+ FTP_FILE file;
+ bool res;
+
+ if(!openFile(path, FTP_FILE_READ_WRITE)) return false;
+ res = file.timestamp(T_WRITE, year, month, day, hour, minute, second);
+ file.close();
+ return res;
+#endif
+}
+
+bool FtpServer::getFileModTime(char* path, uint16_t* pdate, uint16_t* ptime)
+{
+#if STORAGE_TYPE == STORAGE_FATFS
+ return STORAGE_MANAGER.getFileModTime(path, pdate, ptime);
+#else
+ // FTP_FILE file;
+ bool res;
+
+ if(!openFile(path, FTP_FILE_READ)) {
+ return false;
+ }
+ res = getFileModTime(pdate, ptime);
+ file.close();
+ return res;
+#endif
+}
+
+// Assume SD library is SdFat (or family) and file is open
+
+#if STORAGE_TYPE != STORAGE_FATFS
+bool FtpServer::getFileModTime(uint16_t* pdate, uint16_t* ptime)
+{
+#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT
+#if ESP8266
+ return dir.fileTime();
+#else
+ return dir.getLastWrite();
+#endif
+#elif STORAGE_TYPE == STORAGE_SDFAT1 || STORAGE_TYPE == STORAGE_SPIFM
+ dir_t d;
+
+ if(!file.dirEntry(&d)) return false;
+ *pdate = d.lastWriteDate;
+ *ptime = d.lastWriteTime;
+ return true;
+#elif STORAGE_TYPE == STORAGE_SDFAT2
+ return file.getModifyDateTime(pdate, ptime);
+#endif
+ return false;
+}
+#endif
+
+#if STORAGE_TYPE == STORAGE_SD
+bool FtpServer::rename(const char* path, const char* newpath)
+{
+
+ FTP_FILE myFileIn = STORAGE_MANAGER.open(path, FILE_READ);
+ FTP_FILE myFileOut = STORAGE_MANAGER.open(newpath, FILE_WRITE);
+
+ if(myFileOut) {
+ while(myFileIn.available() > 0) {
+ int i = myFileIn.readBytes((char*)buf, FTP_BUF_SIZE);
+ myFileOut.write(buf, i);
+ }
+ // done, close the destination file
+ myFileOut.close();
+ myFileOut = STORAGE_MANAGER.open(newpath, FILE_READ);
+ }
+ bool operation = false;
+
+ DEBUG_PRINT(F("RENAME --> "));
+ DEBUG_PRINT(myFileIn.size());
+ DEBUG_PRINT(" size ");
+ DEBUG_PRINTLN(myFileOut.size());
+
+ if(myFileIn.size() == myFileOut.size()) {
+ operation = true;
+ }
+
+ if(!operation) return operation;
+
+ myFileIn.close();
+ myFileOut.close();
+
+ return remove(path);
+};
+#endif
diff --git a/lib/SimpleFTPServer/FtpServer.h b/lib/SimpleFTPServer/FtpServer.h
new file mode 100644
index 00000000..dfff905d
--- /dev/null
+++ b/lib/SimpleFTPServer/FtpServer.h
@@ -0,0 +1,662 @@
+/*
+ * FtpServer Arduino, esp8266 and esp32 library for Ftp Server
+ * Derived form Jean-Michel Gallego version
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+
+/*******************************************************************************
+ ** **
+ ** DEFINITIONS FOR FTP SERVER **
+ ** **
+ *******************************************************************************/
+
+#ifndef FTP_SERVER_H
+#define FTP_SERVER_H
+
+#define FTP_SERVER_VERSION "2021-11-09"
+
+#include
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+//
+//#if(NETWORK_ESP8266_SD == DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266)
+// #define ESP8266_GT_2_4_2_SD_STORAGE_SELECTED
+// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 NETWORK_ESP8266
+//#endif
+
+#if !defined(FTP_SERVER_NETWORK_TYPE)
+// select Network type based
+ #if defined(ESP8266) || defined(ESP31B)
+ #if(NETWORK_ESP8266_242 == DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266)
+ #define ARDUINO_ESP8266_RELEASE_2_4_2
+
+ #define FTP_SERVER_NETWORK_TYPE_SELECTED NETWORK_ESP8266_242
+
+ #define FTP_SERVER_NETWORK_TYPE NETWORK_ESP8266
+ #else
+ #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266
+ #endif
+
+ #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ESP8266
+ #elif defined(ESP32)
+ #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32
+ #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ESP32
+ #elif defined(ARDUINO_ARCH_SAMD)
+ #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD
+ #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_SAMD
+ #else
+ #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO
+ #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ARDUINO
+ // #define STORAGE_SD_ENABLED
+ #endif
+#endif
+
+#ifndef FTP_SERVER_NETWORK_TYPE_SELECTED
+ #define FTP_SERVER_NETWORK_TYPE_SELECTED FTP_SERVER_NETWORK_TYPE
+#endif
+
+
+#if defined(ESP8266) || defined(ESP31B)
+ #ifndef STORAGE_SD_FORCE_DISABLE
+ #define STORAGE_SD_ENABLED
+ #endif
+ #ifndef STORAGE_SPIFFS_FORCE_DISABLE
+ #define STORAGE_SPIFFS_ENABLED
+ #endif
+#elif defined(ESP32)
+ #ifndef STORAGE_SD_FORCE_DISABLE
+ #define STORAGE_SD_ENABLED
+ #endif
+ #ifndef STORAGE_SPIFFS_FORCE_DISABLE
+ #define STORAGE_SPIFFS_ENABLED
+ #endif
+#else
+ #ifndef STORAGE_SD_FORCE_DISABLE
+ #define STORAGE_SD_ENABLED
+ #endif
+#endif
+
+
+// Includes and defined based on Network Type
+#if(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+
+// Note:
+// No SSL/WSS support for client in Async mode
+// TLS lib need a sync interface!
+
+#if defined(ESP8266)
+#include
+//#include
+#elif defined(ESP32)
+#include
+//#include
+
+#define FTP_CLIENT_NETWORK_CLASS WiFiClient
+//#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure
+#define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+
+#elif defined(ESP31B)
+#include
+#else
+#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
+#endif
+
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)
+
+ #if !defined(ESP8266) && !defined(ESP31B)
+ #error "network type ESP8266 only possible on the ESP mcu!"
+ #endif
+
+ #ifdef ESP8266
+ #include
+ #else
+ #include
+ #endif
+ #define FTP_CLIENT_NETWORK_CLASS WiFiClient
+ //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure
+ #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+ #define NET_CLASS WiFi
+ #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a )))
+ #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a )))
+ #elif(FTP_SERVER_NETWORK_TYPE == NETWORK_W5100)
+
+ #ifdef STM32_DEVICE
+ #define FTP_CLIENT_NETWORK_CLASS TCPClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS TCPServer
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+ #define NET_CLASS Ethernet
+ #else
+ #include
+ #include
+ #define FTP_CLIENT_NETWORK_CLASS EthernetClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS EthernetServer
+ #define NET_CLASS Ethernet
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+ #endif
+
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ENC28J60)
+
+ #include
+
+ #define FTP_CLIENT_NETWORK_CLASS UIPClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS UIPServer
+ #define NET_CLASS Ethernet
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+ //SSLClient client(base_client, TAs, (size_t)TAs_NUM, A5);
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP32)
+
+ #include
+ //#include
+ #define FTP_CLIENT_NETWORK_CLASS WiFiClient
+ //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure
+ #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+ #define NET_CLASS WiFi
+ #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a )))
+ #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a )))
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP32_ETH)
+
+ #include
+ #define FTP_CLIENT_NETWORK_CLASS WiFiClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+ #define NET_CLASS Ethernet
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_WiFiNINA)
+
+ #include
+ #define FTP_CLIENT_NETWORK_CLASS WiFiClient
+ //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiSSLClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+ #define NET_CLASS WiFi
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_SEEED_RTL8720DN)
+
+ #include
+ #define FTP_CLIENT_NETWORK_CLASS WiFiClient
+ //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiSSLClient
+ #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer
+ #define NET_CLASS WiFi
+ #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a )))
+ #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a )))
+#else
+ #error "no network type selected!"
+#endif
+
+#if(STORAGE_TYPE == STORAGE_SPIFFS)
+ #if defined(ESP32)
+// #define FS_NO_GLOBALS
+ #include
+
+ #define FTP_FILE File
+ #define FTP_DIR File
+ #else
+ #ifdef ARDUINO_ESP8266_RELEASE_2_4_2
+ #define FS_NO_GLOBALS
+ #include "FS.h"
+ #define FTP_FILE fs::File
+ #define FTP_DIR fs::Dir
+ #else
+ #include "FS.h"
+ #define FTP_FILE File
+ #define FTP_DIR Dir
+ #endif
+
+ #endif
+
+
+#if ESP8266
+ #define FTP_FILE_READ "r"
+ #define FTP_FILE_READ_ONLY "r"
+ #define FTP_FILE_READ_WRITE "w+"
+ #define FTP_FILE_WRITE_APPEND "a+"
+ #define FTP_FILE_WRITE_CREATE "w+"
+#else
+ #define FTP_FILE_READ "r"
+ #define FTP_FILE_READ_ONLY "r"
+ #define FTP_FILE_READ_WRITE "w"
+ #define FTP_FILE_WRITE_APPEND "a"
+ #define FTP_FILE_WRITE_CREATE "w"
+#endif
+
+ #define STORAGE_MANAGER SPIFFS
+#elif(STORAGE_TYPE == STORAGE_FFAT)
+ #include "FS.h"
+ #include "FFat.h"
+
+ #define STORAGE_MANAGER FFat
+
+ #define FTP_FILE File
+ #define FTP_DIR File
+
+ #define FTP_FILE_READ "r"
+ #define FTP_FILE_READ_ONLY "r"
+ #define FTP_FILE_READ_WRITE "w"
+ #define FTP_FILE_WRITE_APPEND "a"
+ #define FTP_FILE_WRITE_CREATE "w"
+
+#elif(STORAGE_TYPE == STORAGE_LITTLEFS)
+ #if ESP8266
+ #include "LittleFS.h"
+ #define STORAGE_MANAGER LittleFS
+ #define FTP_FILE File
+ #define FTP_DIR Dir
+
+ #define FTP_FILE_READ "r"
+ #define FTP_FILE_READ_ONLY "r"
+ #define FTP_FILE_READ_WRITE "w+"
+ #define FTP_FILE_WRITE_APPEND "a+"
+ #define FTP_FILE_WRITE_CREATE "w+"
+ #else
+ #include "LittleFS.h"
+ #define STORAGE_MANAGER LittleFS
+ #define FTP_FILE File
+ #define FTP_DIR File
+
+ #define FTP_FILE_READ "r"
+ #define FTP_FILE_READ_ONLY "r"
+ #define FTP_FILE_READ_WRITE "w"
+ #define FTP_FILE_WRITE_APPEND "a"
+ #define FTP_FILE_WRITE_CREATE "w"
+ #endif
+#elif(STORAGE_TYPE == STORAGE_SD)
+ #include
+ #include
+
+ #define STORAGE_MANAGER SD
+ #define FTP_FILE File
+ #define FTP_DIR File
+
+ #define FTP_FILE_READ FILE_READ
+ #define FTP_FILE_READ_ONLY FILE_READ
+ #define FTP_FILE_READ_WRITE FILE_WRITE
+#ifdef ESP8266
+ #define FTP_FILE_READ_WRITE FILE_WRITE
+ #define FTP_FILE_WRITE_APPEND FILE_WRITE
+#else
+ #define FTP_FILE_READ_WRITE FILE_WRITE
+ #define FTP_FILE_WRITE_APPEND FILE_APPEND
+#endif
+ #define FTP_FILE_WRITE_CREATE FILE_WRITE
+
+#elif(STORAGE_TYPE == STORAGE_SEEED_SD)
+ #include
+ #define STORAGE_MANAGER SD
+
+ #include "SD/Seeed_SD.h"
+
+
+
+// #define STORAGE_MANAGER SPIFLASH
+// #include "SFUD/Seeed_SFUD.h"
+
+ #define FTP_FILE File
+ #define FTP_DIR File
+
+ #define FTP_FILE_READ FILE_READ
+ #define FTP_FILE_READ_ONLY FILE_READ
+ #define FTP_FILE_READ_WRITE FILE_WRITE
+ #define FTP_FILE_WRITE_APPEND FILE_APPEND
+ #define FTP_FILE_WRITE_CREATE FILE_WRITE
+
+#elif (STORAGE_TYPE == STORAGE_SDFAT1)
+ #include
+ #include
+
+ #define STORAGE_MANAGER sd
+ #define FTP_FILE SdFile
+ #define FTP_DIR SdFile
+ extern SdFat STORAGE_MANAGER;
+
+ #define FTP_FILE_READ O_READ
+ #define FTP_FILE_READ_ONLY O_RDONLY
+ #define FTP_FILE_READ_WRITE O_RDWR
+ #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND
+ #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT
+#elif (STORAGE_TYPE == STORAGE_SDFAT2)
+ #include
+ #include
+
+ #define STORAGE_MANAGER sd
+ #define FTP_FILE FsFile
+ #define FTP_DIR FsFile
+ extern SdFat STORAGE_MANAGER;
+
+ #define FTP_FILE_READ O_READ
+ #define FTP_FILE_READ_ONLY O_RDONLY
+ #define FTP_FILE_READ_WRITE O_RDWR
+ #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND
+ #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT
+#elif (STORAGE_TYPE == STORAGE_SPIFM)
+ #include
+ #include
+ #include
+
+ #define STORAGE_MANAGER fatfs
+ #define FTP_FILE File
+ #define FTP_DIR File
+ extern FatFileSystem STORAGE_MANAGER;
+ extern Adafruit_SPIFlash flash;
+ #define FTP_FILE_READ O_READ
+ #define FTP_FILE_READ_ONLY O_RDONLY
+ #define FTP_FILE_READ_WRITE O_RDWR
+ #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND
+ #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT
+
+#elif (STORAGE_TYPE == STORAGE_FATFS)
+ #include
+ #include
+
+ #define STORAGE_MANAGER sdff
+ #define FTP_FILE FileFs
+ #define FTP_DIR DirFs
+ extern FatFsClass STORAGE_MANAGER;
+ #define O_READ FA_READ
+ #define O_WRITE FA_WRITE
+ #define O_RDWR FA_READ | FA_WRITE
+ #define O_CREAT FA_CREATE_ALWAYS
+ #define O_APPEND FA_OPEN_APPEND
+
+ #define FTP_FILE_READ O_READ
+ #define FTP_FILE_READ_ONLY O_RDONLY
+ #define FTP_FILE_READ_WRITE O_RDWR
+ #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND
+ #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT
+#endif
+
+//#ifdef FTP_CLIENT_NETWORK_SSL_CLASS
+//#define FTP_CLIENT_NETWORK_CLASS FTP_CLIENT_NETWORK_SSL_CLASS
+//#endif
+
+#define OPEN_CLOSE_SPIFFS
+#define OPEN_CLOSE_SD
+
+// Setup debug printing macros.
+#ifdef FTP_SERVER_DEBUG
+ #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }
+ #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }
+#else
+ #define DEBUG_PRINT(...) {}
+ #define DEBUG_PRINTLN(...) {}
+#endif
+
+#define FTP_USER "arduino" // Default user'name
+#define FTP_PASS "test" // Default password
+
+#define FTP_CMD_PORT 21 // Command port on wich server is listening
+#define FTP_DATA_PORT_DFLT 20 // Default data port in active mode
+#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode
+
+#define FF_MAX_LFN 255 // max size of a long file name
+#define FTP_CMD_SIZE FF_MAX_LFN+8 // max size of a command
+#define FTP_CWD_SIZE FF_MAX_LFN+8 // max size of a directory name
+#define FTP_FIL_SIZE FF_MAX_LFN // max size of a file name
+#define FTP_CRED_SIZE 16 // max size of username and password
+#define FTP_NULLIP() IPAddress(0,0,0,0)
+
+enum ftpCmd { FTP_Stop = 0, // In this stage, stop any connection
+ FTP_Init, // initialize some variables
+ FTP_Client, // wait for client connection
+ FTP_User, // wait for user name
+ FTP_Pass, // wait for user password
+ FTP_Cmd }; // answers to commands
+
+enum ftpTransfer { FTP_Close = 0, // In this stage, close data channel
+ FTP_Retrieve, // retrieve file
+ FTP_Store, // store file
+ FTP_List, // list of files
+ FTP_Nlst, // list of name of files
+ FTP_Mlsd }; // listing for machine processing
+
+enum ftpDataConn { FTP_NoConn = 0,// No data connexion
+ FTP_Pasive, // Pasive type
+ FTP_Active }; // Active type
+
+enum FtpOperation {
+ FTP_CONNECT,
+ FTP_DISCONNECT,
+ FTP_FREE_SPACE_CHANGE
+};
+
+enum FtpTransferOperation {
+ FTP_UPLOAD_START = 0,
+ FTP_UPLOAD = 1,
+
+ FTP_DOWNLOAD_START = 2,
+ FTP_DOWNLOAD = 3,
+
+
+ FTP_TRANSFER_STOP = 4,
+ FTP_DOWNLOAD_STOP = 4,
+ FTP_UPLOAD_STOP = 4,
+
+ FTP_TRANSFER_ERROR = 5,
+ FTP_DOWNLOAD_ERROR = 5,
+ FTP_UPLOAD_ERROR = 5
+};
+
+class FtpServer
+{
+public:
+ FtpServer( uint16_t _cmdPort = FTP_CMD_PORT, uint16_t _pasvPort = FTP_DATA_PORT_PASV );
+
+ void begin( const char * _user = FTP_USER, const char * _pass = FTP_PASS, const char * welcomeMessage = "Welcome to Simply FTP server" );
+ void end();
+ void credentials( const char * _user, const char * _pass );
+ uint8_t handleFTP();
+
+ void setCallback(void (*_callbackParam)(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace) )
+ {
+ _callback = _callbackParam;
+ }
+
+ void setTransferCallback(void (*_transferCallbackParam)(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize) )
+ {
+ _transferCallback = _transferCallbackParam;
+ }
+
+private:
+ void (*_callback)(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){};
+ void (*_transferCallback)(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){};
+
+ void iniVariables();
+ void clientConnected();
+ void disconnectClient();
+ bool processCommand();
+ bool haveParameter();
+ int dataConnect( bool out150 = true );
+ bool dataConnected();
+ bool doRetrieve();
+ bool doStore();
+ bool doList();
+ bool doMlsd();
+ void closeTransfer();
+ void abortTransfer();
+ bool makePath( char * fullName, char * param = NULL );
+ bool makeExistsPath( char * path, char * param = NULL );
+ bool openDir( FTP_DIR * pdir );
+ bool isDir( char * path );
+ uint8_t getDateTime( char * dt, 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 );
+ bool timeStamp( char * path, uint16_t year, uint8_t month, uint8_t day,
+ uint8_t hour, uint8_t minute, uint8_t second );
+ bool getFileModTime( char * path, uint16_t * pdate, uint16_t * ptime );
+#if STORAGE_TYPE != STORAGE_FATFS
+ bool getFileModTime( uint16_t * pdate, uint16_t * ptime );
+#endif
+ int8_t readChar();
+
+ const char* getFileName(FTP_FILE *file){
+ #if STORAGE_TYPE <= STORAGE_SDFAT2
+ int max_characters = 100;
+ char f_name[max_characters];
+ file->getName(f_name, max_characters);
+ String filename = String(f_name);
+ return filename.c_str();
+ #elif STORAGE_TYPE == STORAGE_FATFS
+ return file->fileName();
+ #else
+ return file->path();
+ #endif
+ }
+ bool exists( const char * path ) {
+#if STORAGE_TYPE == STORAGE_SPIFFS || (STORAGE_TYPE == STORAGE_SD && FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)
+ if (strcmp(path, "/") == 0) return true;
+#endif
+#if STORAGE_TYPE == STORAGE_FFAT || (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32))
+ FTP_DIR f = STORAGE_MANAGER.open(path, "r");
+ return (f == true);
+#else
+ return STORAGE_MANAGER.exists( path );
+#endif
+ };
+ bool remove( const char * path ) { return STORAGE_MANAGER.remove( path ); };
+#if STORAGE_TYPE == STORAGE_SPIFFS
+ bool makeDir( const char * path ) { return false; };
+ bool removeDir( const char * path ) { return false; };
+#else
+ bool makeDir( const char * path ) { return STORAGE_MANAGER.mkdir( path ); };
+ bool removeDir( const char * path ) { return STORAGE_MANAGER.rmdir( path ); };
+#endif
+
+#if STORAGE_TYPE == STORAGE_SD
+ bool rename( const char * path, const char * newpath );
+#else
+ bool rename( const char * path, const char * newpath ) { return STORAGE_MANAGER.rename( path, newpath ); };
+#endif
+#if (STORAGE_TYPE == STORAGE_SEEED_SD)
+ bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt );
+#elif (STORAGE_TYPE == STORAGE_SD && defined(ESP8266))// FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266_242)
+ bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt );
+#elif (STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT )
+ bool openFile( const char * path, const char * readType );
+// bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt );
+#elif STORAGE_TYPE <= STORAGE_SDFAT2
+ bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt );
+#else
+ bool openFile( char path[ FTP_CWD_SIZE ], const char * readType );
+ bool openFile( const char * path, const char * readType );
+// bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt );
+#endif
+// bool openFile( char path[ FTP_CWD_SIZE ], const char * readType );
+// bool openFile( const char * path, const char * readType );
+ uint16_t fileSize( FTP_FILE file );
+
+#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS
+#if ESP8266
+ uint32_t capacity() {
+ FSInfo fi;
+ STORAGE_MANAGER.info(fi);
+
+ return fi.totalBytes >> 1;
+ };
+ uint32_t free() {
+ FSInfo fi;
+ STORAGE_MANAGER.info(fi);
+
+ return fi.totalBytes -
+ fi.usedBytes >> 1;
+ };
+#else
+ uint32_t capacity() {
+ return STORAGE_MANAGER.totalBytes() >> 1;
+ };
+ uint32_t free() {
+ return (STORAGE_MANAGER.totalBytes() -
+ STORAGE_MANAGER.usedBytes()) >> 1;
+ };
+#endif
+#elif STORAGE_TYPE == STORAGE_SD
+ uint32_t capacity() { return true; };
+ uint32_t free() { return true; };
+#elif STORAGE_TYPE == STORAGE_SEEED_SD
+ uint32_t capacity() {
+ return STORAGE_MANAGER.totalBytes() >> 1;
+ };
+ uint32_t free() {
+ return (STORAGE_MANAGER.totalBytes() -
+ STORAGE_MANAGER.usedBytes()) >> 1;
+ };
+#elif STORAGE_TYPE == STORAGE_SDFAT1
+ uint32_t capacity() { return STORAGE_MANAGER.card()->cardSize() >> 1; };
+ uint32_t free() { return STORAGE_MANAGER.vol()->freeClusterCount() *
+ STORAGE_MANAGER.vol()->sectorsPerCluster() >> 1; };
+#elif STORAGE_TYPE == STORAGE_SDFAT2
+ uint32_t capacity() { return STORAGE_MANAGER.card()->sectorCount() >> 1; };
+ uint32_t free() { return STORAGE_MANAGER.vol()->freeClusterCount() *
+ STORAGE_MANAGER.vol()->sectorsPerCluster() >> 1; };
+#elif STORAGE_TYPE == STORAGE_SPIFM
+ uint32_t capacity() { return flash.size() >> 10; };
+ uint32_t free() { return 0; }; // TODO //
+#elif STORAGE_TYPE == STORAGE_FATFS
+ uint32_t capacity() { return STORAGE_MANAGER.capacity(); };
+ uint32_t free() { return STORAGE_MANAGER.free(); };
+#elif STORAGE_TYPE == STORAGE_FFAT
+ uint32_t capacity() { return STORAGE_MANAGER.totalBytes(); };
+ uint32_t free() { return STORAGE_MANAGER.freeBytes(); };
+#endif
+ bool legalChar( char c ) // Return true if char c is allowed in a long file name
+ {
+ if( c == '"' || c == '*' || c == '?' || c == ':' ||
+ c == '<' || c == '>' || c == '|' )
+ return false;
+#if STORAGE_TYPE == STORAGE_FATFS
+ return 0x1f < c && c < 0xff;
+#else
+ return 0x1f < c && c < 0x7f;
+#endif
+ }
+
+ IPAddress localIp; // IP address of server as seen by clients
+ IPAddress dataIp; // IP address of client for data
+ FTP_SERVER_NETWORK_SERVER_CLASS ftpServer;
+ FTP_SERVER_NETWORK_SERVER_CLASS dataServer;
+
+
+ FTP_CLIENT_NETWORK_CLASS client;
+ FTP_CLIENT_NETWORK_CLASS data;
+
+ FTP_FILE file;
+ FTP_DIR dir;
+
+ ftpCmd cmdStage; // stage of ftp command connexion
+ ftpTransfer transferStage; // stage of data connexion
+ ftpDataConn dataConn; // type of data connexion
+
+ uint8_t __attribute__((packed, aligned(4))) // need to be aligned to 32bit for Esp8266 SPIClass::transferBytes()
+ 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 rnfrName[ FTP_CWD_SIZE ]; // name of file for RNFR command
+ const char * user; // user name
+ const char * pass; // password
+ char command[ 5 ]; // command sent by client
+ bool rnfrCmd; // previous command was RNFR
+ char * parameter; // point to begin of parameters sent by client
+ const char * welcomeMessage;
+ uint16_t cmdPort,
+ pasvPort,
+ dataPort;
+ uint16_t iCL; // pointer to cmdLine next incoming char
+ uint16_t nbMatch;
+
+ uint32_t millisDelay, //
+ millisEndConnection, //
+ millisBeginTrans, // store time of beginning of a transaction
+ bytesTransfered; //
+};
+
+#endif // FTP_SERVER_H
diff --git a/lib/SimpleFTPServer/FtpServerKey.h b/lib/SimpleFTPServer/FtpServerKey.h
new file mode 100644
index 00000000..fef50767
--- /dev/null
+++ b/lib/SimpleFTPServer/FtpServerKey.h
@@ -0,0 +1,82 @@
+/*
+ * FtpServer Arduino, esp8266 and esp32 library for Ftp Server
+ * Derived form Jean-Michel Gallego version
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+/*******************************************************************************
+ ** **
+ ** SETTINGS FOR FTP SERVER **
+ ** **
+ *******************************************************************************/
+
+#ifndef FTP_SERVER_CONFIG_H
+#define FTP_SERVER_CONFIG_H
+
+// Uncomment to enable printing out nice debug messages.
+//#define FTP_SERVER_DEBUG
+
+// Define where debug output will be printed.
+#define DEBUG_PRINTER Serial
+
+#define STORAGE_SDFAT1 0 // Library SdFat version 1.4.x
+#define STORAGE_SDFAT2 1 // Library SdFat version >= 2.0.2
+#define STORAGE_SPIFM 2 // Libraries Adafruit_SPIFlash and SdFat-Adafruit-Fork
+#define STORAGE_FATFS 3 // Library FatFs
+#define STORAGE_SD 4 // Standard SD library (suitable for Arduino esp8266 and esp32
+#define STORAGE_SPIFFS 5 // SPIFFS
+#define STORAGE_LITTLEFS 6 // LITTLEFS
+#define STORAGE_SEEED_SD 7 // Seeed_SD library
+#define STORAGE_FFAT 8 // ESP32 FFAT
+
+#define NETWORK_ESP8266_ASYNC (0)
+#define NETWORK_ESP8266 (1)
+#define NETWORK_ESP8266_242 (6)
+#define NETWORK_W5100 (2)
+#define NETWORK_ENC28J60 (3)
+#define NETWORK_ESP32 (4)
+#define NETWORK_ESP32_ETH (5)
+#define NETWORK_WiFiNINA (7)
+#define NETWORK_SEEED_RTL8720DN (8)
+
+#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266
+ #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 NETWORK_ESP8266
+ #define DEFAULT_STORAGE_TYPE_ESP8266 STORAGE_LITTLEFS
+#endif
+#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32
+ #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 NETWORK_ESP32
+ #define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_SPIFFS
+#endif
+#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO
+ #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO NETWORK_W5100
+ #define DEFAULT_STORAGE_TYPE_ARDUINO STORAGE_SDFAT2
+#endif
+#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO_SAMD
+// Wio Terminal
+// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN
+// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SEEED_SD
+// Arduino SAMD
+ #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_WiFiNINA // NETWORK_SEEED_RTL8720DN // NETWORK_WiFiNINA
+ #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SD // STORAGE_SDFAT2 // STORAGE_SD
+#endif
+
+//#define SD_CS_PIN 4
+// Disconnect client after 5 minutes of inactivity (expressed in seconds)
+#define FTP_TIME_OUT 5 * 60
+
+
+// Wait for authentication for 10 seconds (expressed in seconds)
+#define FTP_AUTH_TIME_OUT 10
+
+
+// Size of file buffer for read/write
+// Transfer speed depends of this value
+// Best value depends on many factors: SD card, client side OS, ...
+// But it can be reduced to 512 if memory usage is critical.
+#define FTP_BUF_SIZE 2048 //1024 // 512
+
+#endif // FTP_SERVER_CONFIG_H
diff --git a/lib/SimpleFTPServer/LICENSE.md b/lib/SimpleFTPServer/LICENSE.md
new file mode 100644
index 00000000..54153141
--- /dev/null
+++ b/lib/SimpleFTPServer/LICENSE.md
@@ -0,0 +1,24 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
+
+You may copy, alter and reuse this code in any way you like, but please leave
+reference to www.mischianti.org in your comments if you redistribute this code.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lib/SimpleFTPServer/README.md b/lib/SimpleFTPServer/README.md
new file mode 100644
index 00000000..1a8130cd
--- /dev/null
+++ b/lib/SimpleFTPServer/README.md
@@ -0,0 +1,90 @@
+# SimpleFTPServer
+
+[Instruction on FTP server on esp8266 and esp32](https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32)
+[Simple FTP Server library now with support for Wio Terminal and SD](https://www.mischianti.org/2021/07/01/simple-ftp-server-library-now-with-support-for-wio-terminal-and-sd/)
+
+Simple FTP Server for
+ - esp8266 (Flash: SPIFFs, LittleFS. SD: SD, SdFat 2)
+ - esp32 (SPIFFS, LITTLEFS, FFAT, SdFat)
+ - Arduino (SD with 8.3 file format, SdFat 2)
+ - Wio Terminal (SdFat 2, and native FAT)
+
+
+When I develop a new solution I'd like to divide the application in layer, and so I'd like focus my attention in only one aspect at time.
+
+
+
+ In detail I separate the REST layer (written inside the microcontroller) and the Front-End (written in Angular, React/Redux or vanilla JS), so I'd like to upload new web interface directly to the microcontroller via FTP.
+
+
+
+
+
+
+
+For static information (Web pages for examples), that not change frequently, esp8266 or esp32 have internal SPIFFS (SPI Flash File System) and you can upload data via Arduino IDE as explained in the article "WeMos D1 mini (esp8266), integrated SPIFFS Filesystem" for esp8266 or "ESP32: integrated SPIFFS FileSystem" for esp32 or with LittleFS "WeMos D1 mini (esp8266), integrated LittleFS Filesystem" but for fast operation and future support It's usefully use FTP.
+
+
+
+
+```cpp
+/*
+ * FtpServer esp8266 and esp32 with SPIFFS
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+#ifdef ESP8266
+#include
+#elif defined ESP32
+#include
+#include "SPIFFS.h"
+#endif
+
+#include
+
+const char* ssid = "YOUR_SSID";
+const char* password = "YOUR_PASS";
+
+
+FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
+
+
+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());
+
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount
+ if (SPIFFS.begin(true)) {
+#elif defined ESP8266
+ if (SPIFFS.begin()) {
+#endif
+ Serial.println("SPIFFS opened!");
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. set ports in ESP8266FtpServer.h (default 21, 50009 for PASV)
+ }
+}
+void loop(void){
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+ // server.handleClient(); //example if running a webserver you still need to call .handleClient();
+
+}
+```
\ No newline at end of file
diff --git a/lib/SimpleFTPServer/SimpleFTPServer.h b/lib/SimpleFTPServer/SimpleFTPServer.h
new file mode 100644
index 00000000..894c359c
--- /dev/null
+++ b/lib/SimpleFTPServer/SimpleFTPServer.h
@@ -0,0 +1,18 @@
+/*
+ * FtpServer Arduino, esp8266 and esp32 library for Ftp Server
+ * Derived form https://github.com/nailbuster/esp8266FTPServer
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+#ifndef SIMPLE_FTP_SERVER_H
+#define SIMPLE_FTP_SERVER_H
+
+#include "FtpServer.h"
+
+#endif
+
+#pragma once
diff --git a/lib/SimpleFTPServer/examples/FTPServer_Arduino_Ethernet/FTPServer_Arduino_Ethernet.ino b/lib/SimpleFTPServer/examples/FTPServer_Arduino_Ethernet/FTPServer_Arduino_Ethernet.ino
new file mode 100644
index 00000000..af178521
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_Arduino_Ethernet/FTPServer_Arduino_Ethernet.ino
@@ -0,0 +1,62 @@
+/*
+ * FtpServer Arduino with Ethernet library and w5100 shield
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+#include
+#include
+#include "SD.h"
+
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xE1 };
+
+// Set the static IP address to use if the DHCP fails to assign
+byte macAddr[] = {0x5e, 0xa4, 0x18, 0xf0, 0x8a, 0xf2};
+IPAddress arduinoIP(192, 168, 1, 177);
+IPAddress dnsIP(192, 168, 1, 1);
+IPAddress gatewayIP(192, 168, 1, 1);
+IPAddress subnetIP(255, 255, 255, 0);
+
+FtpServer ftpSrv;
+
+void setup(void){
+ Serial.begin(115200);
+ delay(2000);
+ // If other chips are connected to SPI bus, set to high the pin connected
+ // to their CS before initializing Flash memory
+ pinMode( 4, OUTPUT );
+ digitalWrite( 4, HIGH );
+ pinMode( 10, OUTPUT );
+ digitalWrite( 10, HIGH );
+
+ Serial.print("Starting SD.");
+ while (!SD.begin(4)) {
+ Serial.print(".");
+ }
+ Serial.println("finish!");
+
+ // start the Ethernet connection:
+ Serial.print("Starting ethernet.");
+ if (Ethernet.begin(mac) == 0) {
+ Serial.println("Failed to configure Ethernet using DHCP");
+ Ethernet.begin(macAddr, arduinoIP, dnsIP, gatewayIP, subnetIP);
+ }else{
+ Serial.println("ok to configure Ethernet using DHCP");
+ }
+
+ Serial.print("IP address ");
+ Serial.println(Ethernet.localIP());
+
+ Serial.println("SPIFFS opened!");
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp.
+}
+void loop(void){
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+}
diff --git a/lib/SimpleFTPServer/examples/FTPServer_Arduino_esp32_SD/FTPServer_Arduino_esp32_SD.ino b/lib/SimpleFTPServer/examples/FTPServer_Arduino_esp32_SD/FTPServer_Arduino_esp32_SD.ino
new file mode 100644
index 00000000..c28dcf0b
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_Arduino_esp32_SD/FTPServer_Arduino_esp32_SD.ino
@@ -0,0 +1,96 @@
+/*
+ * FtpServer esp8266 and esp32 with SD
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+#include
+#include "SD.h"
+
+#include
+
+const char* ssid = "";
+const char* password = "";
+
+
+FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
+
+void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
+ Serial.print(">>>>>>>>>>>>>>> _callback " );
+ Serial.print(ftpOperation);
+ /* FTP_CONNECT,
+ * FTP_DISCONNECT,
+ * FTP_FREE_SPACE_CHANGE
+ */
+ Serial.print(" ");
+ Serial.print(freeSpace);
+ Serial.print(" ");
+ Serial.println(totalSpace);
+
+ // freeSpace : totalSpace = x : 360
+
+ if (ftpOperation == FTP_CONNECT) Serial.println(F("CONNECTED"));
+ if (ftpOperation == FTP_DISCONNECT) Serial.println(F("DISCONNECTED"));
+};
+void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
+ Serial.print(">>>>>>>>>>>>>>> _transferCallback " );
+ Serial.print(ftpOperation);
+ /* FTP_UPLOAD_START = 0,
+ * FTP_UPLOAD = 1,
+ *
+ * FTP_DOWNLOAD_START = 2,
+ * FTP_DOWNLOAD = 3,
+ *
+ * FTP_TRANSFER_STOP = 4,
+ * FTP_DOWNLOAD_STOP = 4,
+ * FTP_UPLOAD_STOP = 4,
+ *
+ * FTP_TRANSFER_ERROR = 5,
+ * FTP_DOWNLOAD_ERROR = 5,
+ * FTP_UPLOAD_ERROR = 5
+ */
+ Serial.print(" ");
+ Serial.print(name);
+ Serial.print(" ");
+ Serial.println(transferredSize);
+};
+
+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());
+
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+ SPI.begin(14, 2, 15, 13); //SCK, MISO, MOSI,SS
+
+ if (SD.begin(13, SPI)) {
+ Serial.println("SD opened!");
+
+ ftpSrv.setCallback(_callback);
+ ftpSrv.setTransferCallback(_transferCallback);
+
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. (default 21, 50009 for PASV)
+ }
+}
+void loop(void){
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+ // server.handleClient(); //example if running a webserver you still need to call .handleClient();
+
+}
diff --git a/lib/SimpleFTPServer/examples/FTPServer_esp8266_esp32/FTPServer_esp8266_esp32.ino b/lib/SimpleFTPServer/examples/FTPServer_esp8266_esp32/FTPServer_esp8266_esp32.ino
new file mode 100644
index 00000000..c67a1ade
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_esp8266_esp32/FTPServer_esp8266_esp32.ino
@@ -0,0 +1,110 @@
+/*
+ * FtpServer esp8266 and esp32 with SPIFFS
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
+ *
+ */
+
+#ifdef ESP8266
+#include
+#elif defined ESP32
+#include
+#include "SPIFFS.h"
+#endif
+
+#include
+
+const char* ssid = "YOUR_SSID";
+const char* password = "YOUR_PASS";
+
+
+FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
+
+void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
+ switch (ftpOperation) {
+ case FTP_CONNECT:
+ Serial.println(F("FTP: Connected!"));
+ break;
+ case FTP_DISCONNECT:
+ Serial.println(F("FTP: Disconnected!"));
+ break;
+ case FTP_FREE_SPACE_CHANGE:
+ Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace);
+ break;
+ default:
+ break;
+ }
+};
+void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
+ switch (ftpOperation) {
+ case FTP_UPLOAD_START:
+ Serial.println(F("FTP: Upload start!"));
+ break;
+ case FTP_UPLOAD:
+ Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize);
+ break;
+ case FTP_TRANSFER_STOP:
+ Serial.println(F("FTP: Finish transfer!"));
+ break;
+ case FTP_TRANSFER_ERROR:
+ Serial.println(F("FTP: Transfer error!"));
+ break;
+ default:
+ break;
+ }
+
+ /* FTP_UPLOAD_START = 0,
+ * FTP_UPLOAD = 1,
+ *
+ * FTP_DOWNLOAD_START = 2,
+ * FTP_DOWNLOAD = 3,
+ *
+ * FTP_TRANSFER_STOP = 4,
+ * FTP_DOWNLOAD_STOP = 4,
+ * FTP_UPLOAD_STOP = 4,
+ *
+ * FTP_TRANSFER_ERROR = 5,
+ * FTP_DOWNLOAD_ERROR = 5,
+ * FTP_UPLOAD_ERROR = 5
+ */
+};
+
+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());
+
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+
+ /////FTP Setup, ensure SPIFFS is started before ftp; /////////
+#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount
+ if (SPIFFS.begin(true)) {
+#elif defined ESP8266
+ if (SPIFFS.begin()) {
+#endif
+ ftpSrv.setCallback(_callback);
+ ftpSrv.setTransferCallback(_transferCallback);
+
+ Serial.println("SPIFFS opened!");
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. (default 21, 50009 for PASV)
+ }
+}
+void loop(void){
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+ // server.handleClient(); //example if running a webserver you still need to call .handleClient();
+
+}
diff --git a/lib/SimpleFTPServer/examples/FTPServer_wio_terminal/FTPServer_wio_terminal.ino b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal/FTPServer_wio_terminal.ino
new file mode 100644
index 00000000..c6620c64
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal/FTPServer_wio_terminal.ino
@@ -0,0 +1,102 @@
+/*
+ * This sketch sends data via HTTP GET requests to examle.com service.
+ */
+
+#include
+#include "SD/Seeed_SD.h"
+
+// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN
+// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SEEED_SD
+
+#include
+
+#include
+
+FtpServer ftpSrv;
+
+const char *ssid = "";
+const char *password = "";
+
+void listDir(const char* dirname, uint8_t levels) {
+ Serial.print("Listing directory: ");
+ Serial.println(dirname);
+
+ File root = SD.open(dirname);
+ if (!root) {
+ Serial.println("Failed to open directory");
+ return;
+ }
+ if (!root.isDirectory()) {
+ Serial.println("Not a directory");
+ return;
+ }
+
+ File file = root.openNextFile();
+ while (file) {
+ if (file.isDirectory()) {
+ Serial.print(" DIR : ");
+ Serial.println(file.name());
+ if (levels) {
+ listDir(file.name(), levels - 1);
+ }
+ } else {
+ Serial.print(" FILE: ");
+ Serial.print(file.name());
+ Serial.print(" SIZE: ");
+ Serial.println(file.size());
+ }
+ file = root.openNextFile();
+ }
+}
+
+
+void setup()
+{
+ Serial.begin(115200);
+ delay(1000);
+
+ pinMode(5, OUTPUT);
+ digitalWrite(5, HIGH);
+
+ while (!SD.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) {
+ Serial.println("Card Mount Failed");
+ return;
+ }
+
+ // We start by connecting to a WiFi network
+ Serial.println();
+ Serial.println();
+ Serial.print("Connecting to ");
+ Serial.print(ssid);
+
+ WiFi.mode(WIFI_STA);
+
+ WiFi.begin(ssid, password);
+
+ while (WiFi.status() != WL_CONNECTED)
+ {
+ Serial.print("Connecting to ");
+ Serial.println(ssid);
+ WiFi.begin(ssid, password);
+ Serial.print(".");
+ delay(500);
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+ delay(1000);
+
+ Serial.print("Starting SD.");
+
+ Serial.println("finish!");
+
+ listDir("/", 0);
+
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp.
+ }
+
+ void loop(void) {
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+ }
diff --git a/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat/FTPServer_wio_terminal_SdFat.ino b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat/FTPServer_wio_terminal_SdFat.ino
new file mode 100644
index 00000000..155f84de
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat/FTPServer_wio_terminal_SdFat.ino
@@ -0,0 +1,98 @@
+/*
+ * This sketch sends data via HTTP GET requests to examle.com service.
+ */
+
+#include "SdFat.h"
+
+#include
+
+#include
+
+// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN
+// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SDFAT2
+
+#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, 2)
+SdFs sd;
+
+FtpServer ftpSrv;
+
+const char *ssid = "";
+const char *password = "";
+
+void setup()
+{
+ Serial.begin(115200);
+ delay(1000);
+
+ pinMode(5, OUTPUT);
+ digitalWrite(5, HIGH);
+
+ // Initialize the SD.
+ if (!sd.begin(SD_CONFIG)) {
+ sd.initErrorHalt(&Serial);
+ }
+ FsFile dir;
+ FsFile file;
+
+ // Open root directory
+ if (!dir.open("/")){
+ Serial.println("dir.open failed");
+ }
+
+ // We start by connecting to a WiFi network
+
+ Serial.println();
+ Serial.println();
+ Serial.print("Connecting to ");
+ Serial.print(ssid);
+
+ WiFi.mode(WIFI_STA);
+
+
+ WiFi.begin(ssid, password);
+
+ while (WiFi.status() != WL_CONNECTED)
+ {
+ Serial.print("Connecting to ");
+ Serial.println(ssid);
+ WiFi.begin(ssid, password);
+ Serial.print(".");
+ delay(500);
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+ delay(1000);
+
+ Serial.print("Starting SD.");
+
+ Serial.println("finish!");
+
+ while (file.openNext(&dir, O_RDONLY)) {
+ file.printFileSize(&Serial);
+ Serial.write(' ');
+ file.printModifyDateTime(&Serial);
+ Serial.write(' ');
+ file.printName(&Serial);
+ if (file.isDir()) {
+ // Indicate a directory.
+ Serial.write('/');
+ }
+ Serial.println();
+ file.close();
+ }
+ if (dir.getError()) {
+ Serial.println("openNext failed");
+ } else {
+ Serial.println("Done!");
+ }
+
+
+ ftpSrv.begin("esp8266","esp8266"); //username, password for ftp.
+ }
+
+ void loop(void) {
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+ }
diff --git a/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat_TFT_monitor/FTPServer_wio_terminal_SdFat_TFT_monitor.ino b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat_TFT_monitor/FTPServer_wio_terminal_SdFat_TFT_monitor.ino
new file mode 100644
index 00000000..86d18b19
--- /dev/null
+++ b/lib/SimpleFTPServer/examples/FTPServer_wio_terminal_SdFat_TFT_monitor/FTPServer_wio_terminal_SdFat_TFT_monitor.ino
@@ -0,0 +1,367 @@
+/*
+ * FtpServer Wio Terminal with SdFat library
+ * and with callbacks to the main actions of FTP server
+ * and a monitor on TFT
+ *
+ * AUTHOR: Renzo Mischianti
+ *
+ * https://www.mischianti.org/
+ *
+ */
+
+#include "SdFat.h"
+
+#include
+
+
+#include // Hardware-specific library
+#include
+
+#include
+
+TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
+
+#define DEG2RAD 0.0174532925
+
+byte inc = 0;
+unsigned int col = 0;
+
+#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, 2)
+SdFs sd;
+
+FtpServer ftpSrv;
+
+const char *ssid = "reef-casa-sopra";
+const char *password = "aabbccdd77";
+
+#define MAIN_TOP 110
+
+#define FREE_SPACE_PIE_X 80
+#define FREE_SPACE_PIE_Y MAIN_TOP+40
+#define FREE_SPACE_PIE_RADIUS 50
+
+void freeSpacePieData(unsigned int freeSpace, unsigned int totalSpace) {
+ int pieFree = 360 - (freeSpace * 360 / totalSpace);
+
+ fillSegment(FREE_SPACE_PIE_X, FREE_SPACE_PIE_Y, 0, pieFree, FREE_SPACE_PIE_RADIUS, TFT_RED);
+ fillSegment(FREE_SPACE_PIE_X, FREE_SPACE_PIE_Y, pieFree, 360 - pieFree, FREE_SPACE_PIE_RADIUS, TFT_BLUE);
+
+ // Set "cursor" at top left corner of display (0,0) and select font 2
+ // (cursor will move to next line automatically during printing with 'tft.println'
+ // or stay on the line is there is room for the text with tft.print)
+ tft.setCursor(FREE_SPACE_PIE_X + 80, MAIN_TOP, 2);
+ // Set the font colour to be white with a black background, set text size multiplier to 1
+ tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(1);
+ // We can now plot text on screen using the "print" class
+ Serial.print(freeSpace/1000);Serial.print("Mb/");Serial.print(String(totalSpace/1000));Serial.println("Mb");
+ tft.print(freeSpace/1000);tft.print("Mb/");tft.print(String(totalSpace/1000));tft.println("Mb");
+}
+
+void connectedDisconnected(bool connected) {
+ tft.fillCircle(FREE_SPACE_PIE_X + 80 + 10, MAIN_TOP+25+7, 10, (connected)?TFT_GREEN:TFT_RED);
+
+ tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25, 2);
+ tft.println(" ");
+
+ tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25, 2);
+ (connected)?tft.println("Connected!"):tft.println("Disconnected!");
+}
+
+void transfer(bool transfer, bool upload) {
+ tft.fillCircle(FREE_SPACE_PIE_X + 80 + 10, MAIN_TOP+25+25+7, 10, (transfer)?(upload)?TFT_GREEN:TFT_BLUE:TFT_RED);
+
+ tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25+25, 2);
+ tft.println(" ");
+
+ tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25+25, 2);
+ (transfer)?tft.println((upload)?"Upload!":"Download!"):tft.println("Idle!");
+}
+
+//index - starting at, n- how many chars
+char* subString(const char *s, int index, int n){
+ char* b = (char*) malloc((strlen(s) + 1) * sizeof(char));
+ strcpy(b,s);
+
+ Serial.println("--------------------------------------");
+ Serial.println(s);
+ Serial.println(index);
+ Serial.println(n);
+ char *res = new char[n + 1];
+ Serial.println(res);
+ sprintf(res, "%.*s", n, b + index);
+ Serial.println(res);
+ free(b);
+ return res;
+}
+
+
+void fileTransfer(FtpTransferOperation ftpOperation, const char* filename, unsigned int transferredSize) {
+ int yoffset = 2;
+
+ tft.setCursor(20, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2);
+ tft.println(F(" "));
+
+ tft.setCursor(20, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2);
+ int lenfile = strlen(filename);
+ Serial.println(lenfile);
+ if (lenfile>22) {
+
+ tft.print(subString(filename, 0, 16));tft.print(F("~"));
+ tft.print( subString(filename, (lenfile-4), 4) );
+ } else {
+ tft.print(filename);
+ }
+ tft.setCursor(20+160, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2);
+ tft.print(F(" "));
+ tft.setCursor(20+160, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2);
+ tft.print(transferredSize);tft.print("Kb");
+
+ tft.setCursor(320-55, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2);
+ switch (ftpOperation) {
+ case FTP_UPLOAD:
+ tft.setTextColor(TFT_GREEN, TFT_BLACK);
+ tft.print(F("Upload"));
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
+ break;
+ case FTP_DOWNLOAD:
+ tft.setTextColor(TFT_BLUE, TFT_BLACK);
+ tft.print(F("Down"));
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
+
+ break;
+ case FTP_TRANSFER_STOP:
+ tft.setTextColor(TFT_GREEN, TFT_BLACK);
+ tft.print(F("OK"));
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
+
+ break;
+ case FTP_TRANSFER_ERROR:
+ tft.setTextColor(TFT_RED, TFT_BLACK);
+ tft.print(F("Error"));
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
+
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+void wifiStrenght (int8_t RSSI, bool connection = false) {
+ Serial.print("RSSI --> ");Serial.println(RSSI);
+ int marginX = 30;
+
+ int startX = 90;
+ int widthW = 320-(startX+marginX);
+
+ int startY = 60;
+ int heightY = 10;
+
+ tft.setCursor(marginX, startY - 5, 2);
+ tft.print(F(" "));
+ tft.setCursor(marginX, startY - 5, 2);
+
+ if (connection) {
+ tft.print(F("Connectint to: "));
+ tft.print(ssid);
+ }else{
+ tft.println("WiFi str: ");
+
+ // 120 : 120-RSSI = 300 : x
+
+ tft.drawRoundRect(startX, startY, widthW, heightY, 5, TFT_WHITE);
+
+ uint32_t colorRSSI = TFT_GREEN;
+ if (abs(RSSI)<55) {
+ colorRSSI = TFT_GREEN;
+ } else if (abs(RSSI)<75) {
+ colorRSSI = TFT_YELLOW;
+ } else if (abs(RSSI)<75) {
+ colorRSSI = TFT_RED;
+ }
+
+ tft.fillRoundRect(startX+1, startY+1, (120+RSSI)*widthW/120, heightY-2, 5, colorRSSI);
+
+ tft.setCursor(marginX, startY + 15, 2);
+
+ tft.print("IP: ");
+ tft.println(WiFi.localIP());
+ }
+}
+
+void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
+ Serial.print(">>>>>>>>>>>>>>> _callback " );
+ Serial.print(ftpOperation);
+ /* FTP_CONNECT,
+ * FTP_DISCONNECT,
+ * FTP_FREE_SPACE_CHANGE
+ */
+ Serial.print(" ");
+ Serial.print(freeSpace);
+ Serial.print(" ");
+ Serial.println(totalSpace);
+
+ // freeSpace : totalSpace = x : 360
+
+ freeSpacePieData(freeSpace, totalSpace);
+
+ if (ftpOperation == FTP_CONNECT) connectedDisconnected(true);
+ if (ftpOperation == FTP_DISCONNECT) connectedDisconnected(false);
+};
+void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
+ Serial.print(">>>>>>>>>>>>>>> _transferCallback " );
+ Serial.print(ftpOperation);
+ /* FTP_UPLOAD_START = 0,
+ * FTP_UPLOAD = 1,
+ *
+ * FTP_DOWNLOAD_START = 2,
+ * FTP_DOWNLOAD = 3,
+ *
+ * FTP_TRANSFER_STOP = 4,
+ * FTP_DOWNLOAD_STOP = 4,
+ * FTP_UPLOAD_STOP = 4,
+ *
+ * FTP_TRANSFER_ERROR = 5,
+ * FTP_DOWNLOAD_ERROR = 5,
+ * FTP_UPLOAD_ERROR = 5
+ */
+ Serial.print(" ");
+ Serial.print(name);
+ Serial.print(" ");
+ Serial.println(transferredSize);
+
+ (ftpOperation==FTP_UPLOAD || ftpOperation==FTP_DOWNLOAD)?transfer(true, ftpOperation==FTP_UPLOAD):transfer(false, false);
+
+ fileTransfer(ftpOperation, name, transferredSize);
+};
+
+
+void setup()
+{
+ ftpSrv.setCallback(_callback);
+ ftpSrv.setTransferCallback(_transferCallback);
+
+ Serial.begin(115200);
+ delay(1000);
+
+ tft.init();
+
+ tft.begin();
+
+ tft.setRotation(3);
+
+ tft.fillScreen(TFT_BLACK);
+
+ tft.setCursor(0, 0);
+
+ tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.setTextSize(2);
+
+ tft.fillRoundRect(3, 3, 320-6, 40, 5, TFT_WHITE);
+
+ tft.drawCentreString("www.mischianti.org", 160, 14,1);
+ tft.setTextColor(TFT_WHITE, TFT_BLACK);
+
+
+ freeSpacePieData(0, 0);
+ connectedDisconnected(false);
+ transfer(false, false);
+
+ wifiStrenght(0, true);
+
+ Serial.println();
+ Serial.println();
+ Serial.print("Connecting to ");
+ Serial.print(ssid);
+
+ WiFi.mode(WIFI_STA);
+
+
+ WiFi.begin(ssid, password);
+
+ while (WiFi.status() != WL_CONNECTED)
+ {
+ Serial.print(".");
+ WiFi.begin(ssid, password);
+ Serial.print(".");
+ tft.print(F("."));
+ delay(500);
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ wifiStrenght(WiFi.RSSI());
+
+ delay(1000);
+
+ if (!sd.begin(SD_CONFIG)) {
+ sd.initErrorHalt(&Serial);
+ }
+ FsFile dir;
+ FsFile file;
+
+ // Open root directory
+ if (!dir.open("/")){
+ Serial.println("dir.open failed");
+ }
+
+ ftpSrv.begin("wioterminal","wioterminal"); //username, password for ftp.
+
+}
+
+void loop() {
+ ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
+}
+
+
+// #########################################################################
+// Draw circle segments
+// #########################################################################
+
+// x,y == coords of centre of circle
+// start_angle = 0 - 359
+// sub_angle = 0 - 360 = subtended angle
+// r = radius
+// colour = 16 bit colour value
+
+int fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour) {
+ // Calculate first pair of coordinates for segment start
+ float sx = cos((start_angle - 90) * DEG2RAD);
+ float sy = sin((start_angle - 90) * DEG2RAD);
+ uint16_t x1 = sx * r + x;
+ uint16_t y1 = sy * r + y;
+
+ // Draw colour blocks every inc degrees
+ for (int i = start_angle; i < start_angle + sub_angle; i++) {
+
+ // Calculate pair of coordinates for segment end
+ int x2 = cos((i + 1 - 90) * DEG2RAD) * r + x;
+ int y2 = sin((i + 1 - 90) * DEG2RAD) * r + y;
+
+ tft.fillTriangle(x1, y1, x2, y2, x, y, colour);
+
+ // Copy segment end to sgement start for next segment
+ x1 = x2;
+ y1 = y2;
+ }
+}
+
+
+// #########################################################################
+// Return the 16 bit colour with brightness 0-100%
+// #########################################################################
+unsigned int brightness(unsigned int colour, int brightness) {
+ byte red = colour >> 11;
+ byte green = (colour & 0x7E0) >> 5;
+ byte blue = colour & 0x1F;
+
+ blue = (blue * brightness) / 100;
+ green = (green * brightness) / 100;
+ red = (red * brightness) / 100;
+
+ return (red << 11) + (green << 5) + blue;
+}
+
diff --git a/lib/SimpleFTPServer/keywords.txt b/lib/SimpleFTPServer/keywords.txt
new file mode 100644
index 00000000..e759b081
--- /dev/null
+++ b/lib/SimpleFTPServer/keywords.txt
@@ -0,0 +1,15 @@
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+SimpleFtpServer KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin KEYWORD2
+credentials KEYWORD2
+setCallback KEYWORD2
+setTransferCallback KEYWORD2
+handleFTP KEYWORD2
\ No newline at end of file
diff --git a/lib/SimpleFTPServer/library.json b/lib/SimpleFTPServer/library.json
new file mode 100644
index 00000000..f73a296b
--- /dev/null
+++ b/lib/SimpleFTPServer/library.json
@@ -0,0 +1,19 @@
+{
+ "name": "SimpleFTPServer",
+ "description": "Simple FTP Server for using esp8266 and esp32 SPIFFs",
+ "keywords": "esp8266, ftp, spiffs",
+ "authors":
+ {
+ "name": "Renzo Mischianti",
+ "email": "renzo.mischianti@gmail.com",
+ "url": "https://www.mischianti.org"
+ },
+ "repository":
+ {
+ "type": "git",
+ "url": "https://github.com/xreef/SimpleFTPServer"
+ },
+ "url": "https://www.mischianti.org",
+ "frameworks": "Arduino",
+ "platforms": "*"
+}
diff --git a/lib/SimpleFTPServer/library.properties b/lib/SimpleFTPServer/library.properties
new file mode 100644
index 00000000..44030841
--- /dev/null
+++ b/lib/SimpleFTPServer/library.properties
@@ -0,0 +1,11 @@
+name=SimpleFTPServer
+version=1.0.1
+author=Renzo Mischianti
+maintainer=Renzo Mischianti
+sentence=Simple FTP server for esp8266, esp32 and Arduino
+paragraph=Simple FTP server for esp8266 (SPIFFS and LittleFS), esp32 (SPIFFS, LittleFS ans FFAT) and Arduino (SD basic lib with 8.3 file format), Wio Terminal (SdFat 2), Arduino MKR (SdFat 2). With internal callback to chck the phase of communication.
+category=Communication
+url=https://www.mischianti.org/category/my-libraries/simple-ftp-server/
+repository=https://github.com/xreef/SimpleFTPServer.git
+architectures=*
+includes=SimpleFTPServer.h
\ No newline at end of file
diff --git a/src/hasp/hasp_dispatch.cpp b/src/hasp/hasp_dispatch.cpp
index 2fbbef2d..0eb6bf8a 100644
--- a/src/hasp/hasp_dispatch.cpp
+++ b/src/hasp/hasp_dispatch.cpp
@@ -1279,6 +1279,14 @@ void dispatch_service(const char*, const char* payload, uint8_t source)
}
#endif
+#if HASP_USE_FTP > 0
+ if(!strcmp_P(payload, "start ftp")) {
+ ftpStart();
+ } else if(!strcmp_P(payload, "stop ftp")) {
+ ftpStop();
+ }
+#endif
+
#if HASP_USE_HTTP > 0 || HASP_USE_HTTP_ASYNC > 0
if(!strcmp_P(payload, "start http")) {
httpStart();
diff --git a/src/hasp_debug.cpp b/src/hasp_debug.cpp
index 8c32100d..bd6e63f3 100644
--- a/src/hasp_debug.cpp
+++ b/src/hasp_debug.cpp
@@ -253,6 +253,9 @@ void debug_get_tag(uint8_t tag, char* buffer)
case TAG_DEBG:
memcpy_P(buffer, PSTR("DBUG"), 5);
break;
+ case TAG_FTP:
+ memcpy_P(buffer, PSTR("FTP "), 5);
+ break;
case TAG_TELN:
memcpy_P(buffer, PSTR("TELN"), 5);
break;
diff --git a/src/hasp_debug.h b/src/hasp_debug.h
index 9c8b59ac..c3d41c81 100644
--- a/src/hasp_debug.h
+++ b/src/hasp_debug.h
@@ -189,6 +189,7 @@ enum {
TAG_MQTT = 65,
TAG_MQTT_PUB = 66,
TAG_MQTT_RCV = 67,
+ TAG_FTP = 68,
TAG_LVGL = 90,
TAG_LVFS = 91,
diff --git a/src/hasp_filesystem.cpp b/src/hasp_filesystem.cpp
index aecee469..7b77ce8c 100644
--- a/src/hasp_filesystem.cpp
+++ b/src/hasp_filesystem.cpp
@@ -141,15 +141,22 @@ void filesystemUnzip(const char*, const char* filename, uint8_t source)
void filesystemInfo()
{ // Get all information of your SPIFFS
+ char used[16] = "";
+ char total[16] = "";
+
#ifdef ESP8266
FSInfo fs_info;
HASP_FS.info(fs_info);
- Log.verbose(TAG_FILE, "Partition size: total: %d, used: %d", fs_info.totalBytes, fs_info.usedBytes);
+ Parser::format_bytes(fs_info.usedBytes(), used, sizeof(used));
+ Parser::format_bytes(fs_info.totalBytes(), total, sizeof(total));
#endif
#ifdef ESP32
- Log.verbose(TAG_FILE, "Partition size: total: %d, used: %d", HASP_FS.totalBytes(), HASP_FS.usedBytes());
+ Parser::format_bytes(HASP_FS.usedBytes(), used, sizeof(used));
+ Parser::format_bytes(HASP_FS.totalBytes(), total, sizeof(total));
#endif
+
+ Log.verbose(TAG_FILE, "Partition size: used: %s / total: %s", total, used);
}
void filesystemList()
diff --git a/src/main_arduino.cpp b/src/main_arduino.cpp
index 8bf1fa23..224b490e 100644
--- a/src/main_arduino.cpp
+++ b/src/main_arduino.cpp
@@ -98,14 +98,18 @@ void setup()
httpSetup();
#endif
-// #if HASP_USE_CONSOLE > 0
-// consoleSetup(); // the consoleSetup is called in debugSetup
-// #endif
+ // #if HASP_USE_CONSOLE > 0
+ // consoleSetup(); // the consoleSetup is called in debugSetup
+ // #endif
#if HASP_USE_TELNET > 0
telnetSetup();
#endif
+#if HASP_USE_FTP > 0
+ ftpSetup();
+#endif
+
#if HASP_USE_TASMOTA_CLIENT > 0
slaveSetup();
#endif
@@ -160,6 +164,10 @@ IRAM_ATTR void loop()
/* Runs Every Second */
haspEverySecond(); // sleep timer & statusupdate
+#if HASP_USE_FTP > 0
+ ftpEverySecond();
+#endif
+
#if HASP_USE_TELNET > 0
telnetEverySecond();
#endif
diff --git a/src/sys/net/hasp_network.cpp b/src/sys/net/hasp_network.cpp
index d183955b..036cf592 100644
--- a/src/sys/net/hasp_network.cpp
+++ b/src/sys/net/hasp_network.cpp
@@ -92,9 +92,13 @@ IRAM_ATTR void networkLoop(void)
mdnsLoop();
#endif // MDNS
+#if HASP_USE_FTP > 0
+ ftpLoop();
+#endif // FTP
+
#if HASP_USE_TELNET > 0
- telnetLoop(); // Console
-#endif // TELNET
+ telnetLoop();
+#endif // TELNET
}
bool networkEvery5Seconds(void)
diff --git a/src/sys/svc/hasp_ftp.cpp b/src/sys/svc/hasp_ftp.cpp
new file mode 100644
index 00000000..3bfb2991
--- /dev/null
+++ b/src/sys/svc/hasp_ftp.cpp
@@ -0,0 +1,169 @@
+/* MIT License - Copyright (c) 2019-2022 Francis Van Roie
+ For full license information read the LICENSE file in the project folder */
+
+#include "hasplib.h"
+
+#if HASP_USE_FTP > 0
+
+#include "hasp_debug.h"
+#include "hasp_config.h"
+#include "hasp_ftp.h"
+#include "hasp_http.h"
+#include "hasp_filesystem.h"
+
+#include "../../hasp/hasp_dispatch.h"
+
+#include "FtpServerKey.h"
+#include "SimpleFTPServer.h"
+
+#if HASP_USE_HTTP > 0 || HASP_USE_HTTP_ASYNC > 0
+extern hasp_http_config_t http_config;
+#endif
+
+FtpServer ftpSrv; // set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
+
+uint16_t ftpPort = 23;
+uint8_t ftpEnabled = true; // Enable telnet debug output
+size_t transferSize = 0;
+const char* transferName = NULL;
+
+void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace)
+{
+ switch(ftpOperation) {
+ case FTP_CONNECT:
+ LOG_VERBOSE(TAG_FTP, F(D_SERVICE_CONNECTED));
+ break;
+ case FTP_DISCONNECT:
+ LOG_VERBOSE(TAG_FTP, F(D_SERVICE_DISCONNECTED));
+ break;
+ case FTP_FREE_SPACE_CHANGE:
+ filesystemInfo();
+ break;
+ default:
+ break;
+ }
+};
+void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize)
+{
+ transferName = name;
+ transferSize = transferredSize;
+
+ switch(ftpOperation) {
+ case FTP_UPLOAD_START: {
+ char size[16];
+ Parser::format_bytes(transferredSize, size, sizeof(size));
+ LOG_VERBOSE(TAG_FTP, "Start upload of file %s (%s)", name, size);
+ return;
+ }
+ case FTP_DOWNLOAD_START: {
+ char size[16];
+ Parser::format_bytes(transferredSize, size, sizeof(size));
+ LOG_VERBOSE(TAG_FTP, "Start download of file %s (%s)", name, size);
+ return;
+ }
+ case FTP_UPLOAD:
+ case FTP_DOWNLOAD:
+ return;
+ case FTP_TRANSFER_STOP: {
+ char size[16];
+ Parser::format_bytes(transferredSize, size, sizeof(size));
+ LOG_VERBOSE(TAG_FTP, "Completed transfer of file %s (%s)", name, size);
+ break;
+ }
+ case FTP_TRANSFER_ERROR:
+ LOG_VERBOSE(TAG_FTP, ("Transfer error!"));
+ break;
+ default:
+ break;
+ }
+
+ transferName = NULL;
+ transferSize = 0;
+
+ /* FTP_UPLOAD_START = 0,
+ * FTP_UPLOAD = 1,
+ *
+ * FTP_DOWNLOAD_START = 2,
+ * FTP_DOWNLOAD = 3,
+ *
+ * FTP_TRANSFER_STOP = 4,
+ * FTP_DOWNLOAD_STOP = 4,
+ * FTP_UPLOAD_STOP = 4,
+ *
+ * FTP_TRANSFER_ERROR = 5,
+ * FTP_DOWNLOAD_ERROR = 5,
+ * FTP_UPLOAD_ERROR = 5
+ */
+};
+
+void ftpStop(void)
+{
+ // LOG_WARNING(TAG_FTP, F("Service cannot be stopped"));
+ ftpSrv.end();
+ LOG_INFO(TAG_FTP, F(D_SERVICE_STOPPED));
+}
+
+void ftpStart()
+{
+ LOG_TRACE(TAG_FTP, F(D_SERVICE_STARTING));
+ ftpSrv.setCallback(_callback);
+ ftpSrv.setTransferCallback(_transferCallback);
+
+#if HASP_USE_HTTP > 0 || HASP_USE_HTTP_ASYNC > 0
+ ftpSrv.begin(http_config.username, http_config.password); // Password must be non-empty
+#else
+ ftpSrv.begin("ftpuser", "haspadmin"); // username, password for ftp. (default 21, 50009 for PASV)
+#endif
+
+ LOG_VERBOSE(TAG_FTP, F(FTP_SERVER_VERSION));
+ LOG_INFO(TAG_FTP, F(D_SERVICE_STARTED));
+}
+
+void ftpSetup()
+{
+#if HASP_START_FTP
+ ftpStart();
+#endif
+}
+
+IRAM_ATTR void ftpLoop()
+{
+ ftpSrv.handleFTP(); // make sure in loop you call handleFTP()!!
+}
+
+void ftpEverySecond(void)
+{
+ if(!transferSize || !transferName) return;
+
+ char size[16];
+ Parser::format_bytes(transferSize, size, sizeof(size));
+ LOG_VERBOSE(TAG_FTP, D_BULLET "%s (%s)", transferName, size);
+}
+
+#if HASP_USE_CONFIG > 0
+bool ftpGetConfig(const JsonObject& settings)
+{
+ bool changed = false;
+
+ if(changed) configOutput(settings, TAG_FTP);
+ return changed;
+}
+
+/** Set FTP Configuration.
+ *
+ * Read the settings from json and sets the application variables.
+ *
+ * @note: read config.json into memory
+ *
+ * @param[in] settings JsonObject with the config settings.
+ **/
+bool ftpSetConfig(const JsonObject& settings)
+{
+ configOutput(settings, TAG_FTP);
+ bool changed = false;
+
+ return changed;
+}
+#endif // HASP_USE_CONFIG
+
+#endif
\ No newline at end of file
diff --git a/src/sys/svc/hasp_ftp.h b/src/sys/svc/hasp_ftp.h
new file mode 100644
index 00000000..eb00f1c0
--- /dev/null
+++ b/src/sys/svc/hasp_ftp.h
@@ -0,0 +1,28 @@
+/* MIT License - Copyright (c) 2019-2022 Francis Van Roie
+ For full license information read the LICENSE file in the project folder */
+
+#ifndef HASP_FTP_H
+#define HASP_FTP_H
+
+#if HASP_USE_FTP > 0
+
+/* ===== Default Event Processors ===== */
+void ftpSetup();
+IRAM_ATTR void ftpLoop(void);
+void ftpEvery5Seconds(void);
+void ftpEverySecond(void);
+void ftpStart(void);
+void ftpStop(void);
+
+/* ===== Special Event Processors ===== */
+
+/* ===== Getter and Setter Functions ===== */
+
+/* ===== Read/Write Configuration ===== */
+#if HASP_USE_CONFIG > 0
+bool ftpSetConfig(const JsonObject& settings);
+bool ftpGetConfig(const JsonObject& settings);
+#endif
+
+#endif
+#endif // HASP_FTP_H
diff --git a/user_setups/esp32/_esp32.ini b/user_setups/esp32/_esp32.ini
index 3160a471..8e68e5ec 100644
--- a/user_setups/esp32/_esp32.ini
+++ b/user_setups/esp32/_esp32.ini
@@ -43,7 +43,14 @@ build_flags =
-D USE_LVGL_FREETYPE_MAX_SIZES=4 ; max number of sizes in cache
-D USE_LVGL_FREETYPE_MAX_BYTES=16384 ; max bytes in cache
-D USE_LVGL_FREETYPE_MAX_BYTES_PSRAM=65536 ; max bytes in cache when using PSRAM
-
+; -- SimpleFTpServer build options -----------------
+ -D HASP_USE_FTP=1
+ ;-D FTP_SERVER_DEBUG
+ -D FTP_SERVER_NETWORK_TYPE=4 ;// NETWORK_ESP32
+ -D STORAGE_TYPE=6 ;// STORAGE_LITTLEFS
+ -D STORAGE_SD_FORCE_DISABLE
+ -D STORAGE_SPIFFS_FORCE_DISABLE
+
lib_ignore =
GxTFT
XPT2046_Touchscreen