Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LittleFS files can not be accessed using ESP8266FtpServer #6775

Closed
bill-orange opened this issue Nov 14, 2019 · 16 comments
Closed

LittleFS files can not be accessed using ESP8266FtpServer #6775

bill-orange opened this issue Nov 14, 2019 · 16 comments

Comments

@bill-orange
Copy link

bill-orange commented Nov 14, 2019

  • Hardware: Adafruit Huzzah ESP8266
  • Core Version: 2.6.1
  • Development Env: Arduino IDE 1.8.9
  • Operating System: Windows| 10

Settings in IDE

  • Module: Huzzah ESP8266 Module
  • Flash Mode: unknown
  • Flash Size: 4MB
  • lwip Variant: v2 Lower Memory
  • Reset Method: unknown
  • Flash Frequency: 40Mhz
  • CPU Frequency: 80Mhz|
  • Upload Using: SERIAL
  • Upload Speed: 115200

Problem Description: Files system created with LittleFS can not be accessed with FTPServer

To avoid the possibility of weird memory interactions, I started with the Huzzah completely erased from the command line.

Detailed problem description: Both LittleFS and FTP file Server appear to be running normally. Files in LittleFS can be created and appended as expected. FTP Server appears to run normally. A connection to it can be quickly achieved. The problem is that the files created with LittleFS are not 'seen' by the FTP Sever. In other words the FTP server directory is empty.

Example:

/* Example showing timestamp support in LittleFS */
/* modified for FTP test - Bill Webb 11/14/19 */
/* Released into the public domain. */
/* Earle F. Philhower, III <earlephilhower@yahoo.com> */

#include <FS.h>
#include <LittleFS.h>
#include <time.h>
#include <ESP8266WiFi.h>
#include <ESP8266FtpServer.h>

#ifndef STASSID
#define STASSID "testnet"
#define STAPSK  "tester12"
#endif

const char *ssid = STASSID;
const char *pass = STAPSK;

long timezone = -8;
byte daysavetime = 0;
bool goodTime = false;

FtpServer ftpSrv;


bool getLocalTime(struct tm * info, uint32_t count) {
  time_t now;

  time(&now);
  localtime_r(&now, info);
 
  while (info->tm_year < 80 || info->tm_year > 135) {
    delay(500);
    time(&now);
    localtime_r(&now, info);
    count--;
    //Serial.print(" year ");Serial.print(info->tm_year);
    Serial.print("+");
    if (count >60) {
      Serial.print("................  no time sync");
      return false;
    }
  }
  return true;
}


void listDir(const char * dirname) {
  Serial.printf("Listing directory: %s\n", dirname);

  Dir root = LittleFS.openDir(dirname);

  while (root.next()) {
    File file = root.openFile("r");
    Serial.print("  FILE: ");
    Serial.print(root.fileName());
    Serial.print("  SIZE: ");
    Serial.print(file.size());
    time_t t = file.getLastWrite();
    struct tm * tmstruct = localtime(&t);
    file.close();
    Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
  }
}


void readFile(const char * path) {
  Serial.printf("Reading file: %s\n", path);

  File file = LittleFS.open(path, "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(const char * path, const char * message) {
  Serial.printf("Writing file: %s\n", path);

  File file = LittleFS.open(path, "w");
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(const char * path, const char * message) {
  Serial.printf("Appending to file: %s\n", path);

  File file = LittleFS.open(path, "a");
  if (!file) {
    Serial.println("Failed to open file for appending");
    return;
  }
  if (file.print(message)) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(const char * path1, const char * path2) {
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (LittleFS.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(const char * path) {
  Serial.printf("Deleting file: %s\n", path);
  if (LittleFS.remove(path)) {
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void setup() {
  Serial.begin(115200);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Contacting Time Server");
  configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
  struct tm tmstruct ;
  delay(2000);
  tmstruct.tm_year = 0;
  goodTime = getLocalTime(&tmstruct, 30);
  Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
  Serial.println("");

  Serial.printf("Formatting LittleFS filesystem\n");
  
  if (!LittleFS.begin()) {
    Serial.println("An Error has occurred while mounting Little File System - reformatting");
    LittleFS.format();
    return;
  }
  if (LittleFS.begin()) {
    Serial.println("Little File System opened!");
    ftpSrv.begin("esp", "esp");
    Serial.println("FTP Running");
  }
  
  writeFile("/hello.txt", "Hello ");
  appendFile("/hello.txt", "World!\n");
  listDir("/");

  if (goodTime)Serial.printf("Timestamp should be valid, data should be good.\n");
  readFile("/hello.txt");
  listDir("/");

}

void loop() { 
  
  ftpSrv.handleFTP();
}

Additional Information:

Is LittleFS not designed to work with FTP Server? Should another FTP Server library be used? Is there an FTP setting beyond the defaults that must be adjusted.

@bill-orange bill-orange changed the title LittleFS files can not be accesses using ESP8266FtpServer LittleFS files can not be accessed using ESP8266FtpServer Nov 14, 2019
@earlephilhower
Copy link
Collaborator

Without an example you're not going to be able to get much help.

That said, the FTP server library probably simply needs to have a search/replace done on SPIFFS->LittleFS. File opens, directory lists, etc. all come from the FS object, and it probably is hardcoded in the library as SPIFFS. The LittleFS API is the same, so it's a matter of search and replace (possibly including <LittleFS.h> in the source files of course).

@bill-orange
Copy link
Author

bill-orange commented Nov 14, 2019

@earlephilhower So you are saying the LittleFS library needs to be modified or the FTP Library needs to be modified for my needs?

@earlephilhower
Copy link
Collaborator

FTP library needs to use LittleFS.openDir()/etc. instead of SPIFFS.openDir().

A good library would take the FS* as an input on creation, and just use that and let you go between as you choose (it would also then work with SD cards, too, with SDFS).

@bill-orange
Copy link
Author

bill-orange commented Nov 14, 2019

@earlephilhower Got It, I will write up a sketch illustrating the problem. It should only take a few minutes.

** EDIT

Original post now contains an example.

@earlephilhower
Copy link
Collaborator

@bill-orange Got it, but the problem lies not in the Arduino code, but in the ftpServer itself.

I assume the lib is here: https://github.com/nailbuster/esp8266FTPServer

If you look at the library, it hardcodes SPIFFS.xxxx everywhere. Just edit that library to swap in LittleFS.xxx and you have a reasonably good chance of making it work. Nothing we can do on our end if the library is specifically calling SPIFFS.xxxx.

@devyte
Copy link
Collaborator

devyte commented Nov 14, 2019

I don't see a core issue here. Closing.

On a side note, I wasn't aware of that ftp lib. It looks good, but needs some work to improve it in terms of language use, heap use, generalization of the FS to use, and externalization of the specific WiFiClient/server object to use (e.g.: allow passing a WiFiClientSecure, like HttpClient). It would be interesting if somebody pursued that.
Reference: #1183 .

@devyte devyte closed this as completed Nov 14, 2019
@bill-orange
Copy link
Author

@earlephilhower Sadly, it does not appear to be that simple. I substituted LittleFS for SPIFFS wherever it was used and added the LittleFS library to the .cpp file. It compiles and runs but the directory lists hello.txt as ello.txt and it is inaccessible. This is likely to be beyond my skill level to fix.

@hojatm61
Copy link

Hi.
Inside the ESP8266FtpServer.cpp file
// fn.remove (0, 1);
Comment. Your problem will be solved.

@jmwislez
Copy link

Updated it to do LITTLEFS/SPIFFS on ESP8266/ESP32: https://github.com/jmwislez/ESP32FtpServer

@bill-orange
Copy link
Author

bill-orange commented Nov 1, 2020 via email

@kmsmi
Copy link

kmsmi commented Nov 10, 2020

Updated it to do LITTLEFS/SPIFFS on ESP8266/ESP32: https://github.com/jmwislez/ESP32FtpServer
I have a problem with this library.
I can't upload files that are slightly larger than 4 KB.
This is the problem with SPIFFS and LittleFS.

@bill-orange
Copy link
Author

I am using FTP for files around 120k just fine. Could you post a sketch that is not working for you? I can give it a try.

@kmsmi
Copy link

kmsmi commented Nov 10, 2020

A simple sketch.
#include <ESPFtpServer.h>
#include <LittleFS.h>
setup
ftpSrv.begin(config_sys.ftp_login, config_sys.ftp_pass);
loop
ftpSrv.handleFTP(LittleFS);

ftp works and all files are visible, but files larger than 4 KB are written with an error.
I again returned to ESP8266FtpServer, after editing the source code as described above, everything works fine.

@bill-orange
Copy link
Author

Ah, I am using a different littleFS library. Try this one (note the caps in the .h file).

https://github.com/lorol/LITTLEFS

This code runs fine with bigger files.

#include <ESPFtpServer.h>
#include <LITTLEFS.h> 
#include <WiFi.h>  
#define FS_NAME "LittleFS"
#define FS_ID LITTLEFS
#define FORMAT_LITTLEFS_IF_FAILED false  // if false don't reformat if something goes wrong

const char* ssid = "yours";          // WiFi credentials
const char* password = "yours";     // WiFi credentials
FtpServer ftpSrv;  

void setup() {
  Serial.begin (115200);  // Standart intro boilerplate follows
  WiFi.begin (ssid, password);
  Serial.println ("");
  Serial.print ("Opening WiFi Connection ");

  // 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 ());

  if (!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
    Serial.println("LITTLEFS Mount Failed");
    return;
  }
  // Set-up FTP
  if (FS_ID.begin ()) {
    Serial.println ("File system opened (" + String (FS_NAME) + ")");
    ftpSrv.begin ("esp32", "esp32");    //username, password for ftpon default port 21
  }
  else {
    Serial.println ("File system could not be opened; ftp server will not work");
  }
}
void loop() {
  ftpSrv.handleFTP(LITTLEFS);
}

@kmsmi
Copy link

kmsmi commented Dec 17, 2020

I solved my problem with this library https://github.com/jmwislez/ESPFtpServer
Changed the function boolean FtpServer::doStore()

boolean FtpServer::doStore()
{
// Avoid blocking by never reading more bytes than are available
int navail = data.available();

if (navail > 0)
{
// And be sure not to overflow buf.
if (navail > FTP_BUF_SIZE)
navail = FTP_BUF_SIZE;
int16_t nb = data.read((uint8_t *)buf, navail);
// int16_t nb = data.readBytes((uint8_t*) buf, FTP_BUF_SIZE );
if (nb > 0)
{
// Serial.println( millis() << " " << nb << endl;
file.write((uint8_t *)buf, nb);
bytesTransferred += nb;
}
}
if (!data.connected() && (navail <= 0))
{
closeTransfer();
return false;
}
else
{
return true;
}
}

Now it works fine.

@jmwislez
Copy link

Thank you @kmsmi ! I applied your patch in https://github.com/jmwislez/ESPFtpServer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants