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

Add FTP Client library #1183

Closed
rudi48 opened this issue Dec 9, 2015 · 50 comments · Fixed by #7336
Closed

Add FTP Client library #1183

rudi48 opened this issue Dec 9, 2015 · 50 comments · Fixed by #7336

Comments

@rudi48
Copy link

rudi48 commented Dec 9, 2015

Hi, I like to adapt http://playground.arduino.cc/Code/FTP to ESP8266.
Most part of the adapted program WiFi_FTP_Client.ino works, but there is a compiler error left,
which I can not resolve.
Most part of the program works, except the file data transfer to the FTP server.
If I declare const char clientBuf[64] = "aa";
instead of byte clientBuf[64];, it works, but that is not what I need.
If I use the library Ethernet.h instead of ESP8266WiFi.h it compiles OK, but no data transfer.
By the way, if I compile the original program FTP.ino with Ethernet.h, I get no compiler error.
Please see the error messages (Arduino 1.6.5 and 1.6.6):

In file included from /Volumes/DAT/Users/rudi/Documents/Arduino/hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h:32:0,

                 from /Volumes/DAT/Users/rudi/Documents/Arduino/wifi_FTP_client/wifi_FTP_client.ino:10:
/Volumes/DAT/Users/rudi/Documents/Arduino/hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/WiFiClient.h: In instantiation of 'size_t WiFiClient::write(T&, size_t) [with T = unsigned char [64]; size_t = unsigned int]':

/Volumes/DAT/Users/rudi/Documents/Arduino/wifi_FTP_client/wifi_FTP_client.ino:285:34:   required from here

/Volumes/DAT/Users/rudi/Documents/Arduino/hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/WiFiClient.h:119:36: error: request for member 'available' in 'source', which is of non-class type 'unsigned char [64]'

     size_t left = source.available();
                                    ^
/Volumes/DAT/Users/rudi/Documents/Arduino/hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/WiFiClient.h:123:5: error: request for member 'read' in 'source', which is of non-class type 'unsigned char [64]'
     source.read(buffer.get(), will_send);
     ^

Who can help me to solve that puzzle please?
Regards, Rudolf

The program with the error, the error lines are marked:

/*
   FTP passive client for IDE v1.0.1 and w5100/w5200
   http://playground.arduino.cc/Code/FTP
   Modified 6 June 2015 by SurferTim

   You can pass flash-memory based strings to Serial.print() by wrapping them with F().

   2015-12-09 Rudolf Reuter, adapted to ESP8266 NodeMCU
*/
#include <ESP8266WiFi.h>
// #include <Ethernet.h>
#include <FS.h>

// comment out next line to write to SD from FTP server
#define FTPWRITE

// Set these to your desired softAP credentials. They are not configurable at runtime.
const char *ssid = "FRITZ-7170R";
const char *password = ".............";

boolean debug = false;  // true = more messages
//boolean debug = true;

// LED is needed for failure signalling
const short int BUILTIN_LED2 = 16;  //GPIO16 on NodeMCU (ESP-12)

unsigned long startTime = millis();

// provide text for the WiFi status
const char *str_status[]= {
  "WL_IDLE_STATUS",
  "WL_NO_SSID_AVAIL",
  "WL_SCAN_COMPLETED",
  "WL_CONNECTED",
  "WL_CONNECT_FAILED",
  "WL_CONNECTION_LOST",
  "WL_DISCONNECTED"
};

// provide text for the WiFi mode
const char *str_mode[]= { "WIFI_OFF", "WIFI_STA", "WIFI_AP", "WIFI_AP_STA" };

// this must be unique
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x59, 0x67 };

// change to your network settings
IPAddress ip( 192, 168, 17, 231 );
IPAddress gateway( 192, 168, 17, 1 );
IPAddress subnet( 255, 255, 255, 0 );

// change to your server
IPAddress server( 192, 168, 17, 72 );

WiFiClient client;
WiFiClient dclient;
//EthernetClient client;  // from Arduino FTP.ino
//EthernetClient dclient; // from Arduino FTP.ino

char outBuf[128];
char outCount;

// change fileName to your file (8.3 format!)
String fileName = "TimeTemp.txt";
String  path = "/TimeTemp.txt";

// SPIFFS file handle
File fh;

void signalError() {  // loop endless with LED blinking in case of error
  while(1) {
      digitalWrite(BUILTIN_LED2, LOW);
      delay(300); // ms
      digitalWrite(BUILTIN_LED2, HIGH);
      delay(300); // ms
  }
}

//format bytes
String formatBytes(size_t bytes) {
  if (bytes < 1024) {
    return String(bytes) + "B";
  } else if (bytes < (1024 * 1024)) {
    return String(bytes / 1024.0) + "KB";
  } else if (bytes < (1024 * 1024 * 1024)) {
    return String(bytes / 1024.0 / 1024.0) + "MB";
  } else {
    return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
  }
}

//----------------------- WiFi handling
void connectWifi() {
  Serial.print("Connecting as wifi client to SSID: ");
  Serial.println(ssid);

  // use in case of mode problem
  WiFi.disconnect();
  // switch to Station mode
  if (WiFi.getMode() != WIFI_STA) {
    WiFi.mode(WIFI_STA);
  }

  WiFi.begin ( ssid, password );

  if (debug ) WiFi.printDiag(Serial);

  // ... Give ESP 10 seconds to connect to station.
  unsigned long startTime = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  // Check connection
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("WiFi connected; IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.print("WiFi connect failed to ssid: ");
    Serial.println(ssid);
    Serial.print("WiFi password <");
    Serial.print(password);
    Serial.println(">");
    Serial.println("Check for wrong typing!");
  }
}  // connectWiFi()

//----------------- FTP fail
void efail() {
  byte thisByte = 0;

  client.println(F("QUIT"));

  while (!client.available()) delay(1);

  while (client.available()) {
    thisByte = client.read();
    Serial.write(thisByte);
  }

  client.stop();
  Serial.println(F("Command disconnected"));
  fh.close();
  Serial.println(F("SD closed"));
}  // efail

//-------------- FTP receive
byte eRcv() {
  byte respCode;
  byte thisByte;

  while (!client.available()) delay(1);

  respCode = client.peek();

  outCount = 0;

  while (client.available()) {
    thisByte = client.read();
    Serial.write(thisByte);

    if (outCount < 127) {
      outBuf[outCount] = thisByte;
      outCount++;
      outBuf[outCount] = 0;
    }
  }

  if (respCode >= '4') {
    efail();
    return 0;
  }
  return 1;
}  // eRcv()

//--------------- FTP handling
byte doFTP() {

#ifdef FTPWRITE
  fh = SPIFFS.open(path, "r");
#else
  SPIFFS.remove(path);
  fh = SPIFFS.open(path, "w");
#endif

  if (!fh) {
    Serial.println(F("SPIFFS open fail"));
    return 0;
  }

#ifndef FTPWRITE
  if (!fh.seek(0)) {
    Serial.println(F("Rewind fail"));
    fh.close();
    return 0;
  }
#endif

  Serial.println(F("SPIFFS opened"));

  if (client.connect(server, 21)) {  // 21 = FTP server
    Serial.println(F("Command connected"));
  } else {
    fh.close();
    Serial.println(F("Command connection failed"));
    return 0;
  }

  if (!eRcv()) return 0;
  Serial.println("Send USER");
  client.println(F("USER rudi"));

  if (!eRcv()) return 0;
  Serial.println("Send PASSWORD");
  client.println(F("PASS xxxxxxxx"));

  if (!eRcv()) return 0;
  Serial.println("Send SYST");
  client.println(F("SYST"));

  if (!eRcv()) return 0;
  Serial.println("Send Type I");
  client.println(F("Type I"));

  if (!eRcv()) return 0;
  Serial.println("Send PASV");
  client.println(F("PASV"));

  if (!eRcv()) return 0;

  char *tStr = strtok(outBuf, "(,");
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) {
    tStr = strtok(NULL, "(,");
    array_pasv[i] = atoi(tStr);
    if (tStr == NULL) {
      Serial.println(F("Bad PASV Answer"));
    }
  }
  unsigned int hiPort, loPort;
  hiPort = array_pasv[4] << 8;
  loPort = array_pasv[5] & 255;

  Serial.print(F("Data port: "));
  hiPort = hiPort | loPort;
  Serial.println(hiPort);

  if (dclient.connect(server, hiPort)) {
    Serial.println(F("Data connected"));
  }
  else {
    Serial.println(F("Data connection failed"));
    client.stop();
    fh.close();
    return 0;
  }

#ifdef FTPWRITE
  Serial.println("Send STOR filename");
  client.print(F("STOR "));
  client.println(fileName);
#else
  client.print(F("RETR "));
  client.println(fileName);
#endif

  if (!eRcv()) {
    dclient.stop();
    return 0;
  }

#ifdef FTPWRITE
  Serial.println(F("Writing"));
  //byte clientBuf[64];
  //const char clientBuf[64] = "aa";
  uint8_t clientBuf[64];
  //char clientBuf[64];
  //unsigned int clientCount = 0;
  size_t clientCount = 0;

  while (fh.available()) {
    clientBuf[clientCount] = fh.read();
    clientCount++;
    if (clientCount > 63) {
      dclient.write(clientBuf, 64);  //------------ ERROR
      clientCount = 0;
      delay(1);
    }
  }
  if (clientCount > 0) dclient.write(clientBuf, clientCount); //----- ERROR

#else
  while (dclient.connected()) {
    while (dclient.available()) {
      char c = dclient.read();
      fh.write(c);
      Serial.write(c);
    }
  }
#endif

  dclient.stop();
  Serial.println(F("Data disconnected"));

  if (!eRcv()) return 0;

  client.println(F("QUIT"));

  if (!eRcv()) return 0;

  client.stop();
  Serial.println(F("Command disconnected"));

  fh.close();
  Serial.println(F("SPIFS closed"));
  return 1;
}  // doFTP()


void readSPIFFS() {
  fh = SPIFFS.open(fileName, "r");

  if (!fh) {
    Serial.println(F("SPIFFS open fail"));
    return;
  }

  while (fh.available()) {
    Serial.write(fh.read());
  }

  fh.close();
}  // readSPIFFS()


void setup() {
  delay(1000);
  Serial.begin(115200);
  delay(1000);
  Serial.println("Sync,Sync,Sync,Sync,Sync");
  delay(500);
  Serial.println();
  // signal start
  pinMode(BUILTIN_LED2, OUTPUT);
  digitalWrite(BUILTIN_LED2, LOW);
  delay(100); // ms
  digitalWrite(BUILTIN_LED2, HIGH);
  delay(300); // ms

  Serial.print("Chip ID: 0x");
  Serial.println(ESP.getChipId(), HEX);

  Serial.println ( "Connect to Router requested" );
  connectWifi();
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("WiFi mode: ");
    Serial.println(str_mode[WiFi.getMode()]);
    Serial.print ( "Status: " );
    Serial.println (str_status[WiFi.status()]);
    // signal WiFi connect
    digitalWrite(BUILTIN_LED2, LOW);
    delay(300); // ms
    digitalWrite(BUILTIN_LED2, HIGH);      
  } else {
    Serial.println("");
    Serial.println("WiFi connect failed, push RESET button.");
    signalError();
  }

  //Ethernet.begin(mac, ip, gateway, gateway, subnet);  // from Arduino FTP.ino

  if (!SPIFFS.begin()) {
     Serial.println("SPIFFS failed, needs formatting");
     signalError();
  }

  fh = SPIFFS.open(path, "r");
  if (!fh) {
    Serial.println(F("SPIFFS open fail"));
    signalError();
  } else fh.close();

  Serial.println(F("Ready. Press f or r"));
}  // setup()


void loop() {
  byte inChar;

  if (Serial.available() > 0) {
    inChar = Serial.read();
  }

  if (inChar == 'f') {
    if (doFTP()) Serial.println(F("FTP OK"));
    else Serial.println(F("FTP FAIL"));
  }

  if (inChar == 'r') {
    String fileNameDir;
    Dir dir = SPIFFS.openDir("/");
    while (dir.next()) {
      fileNameDir = dir.fileName();
      size_t fileSize = dir.fileSize();
      Serial.printf("FS File: %s, size: %s\n", fileNameDir.c_str(), formatBytes(fileSize).c_str());
    }
  }
  delay(10);
}

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@Links2004
Copy link
Collaborator

try to use it like this

dclient.write((const uint8_t *) &clientBuf[0], 64);

for a faster upload you shut increase the 64 to 1460.

@rudi48
Copy link
Author

rudi48 commented Dec 10, 2015

Hello Markus,
Thank you very much, now the compiler error is gone :-)
I gnawed already 3 days at that problem.
After the patch the program works as expected.
I could upload my first file (35 KB) to the FTP server. Jubel!

One question of curiosity, how do found out, what to do?
Is there a tutorial describing that?
For me it is still a mystery.
Regards, Rudolf

@Links2004
Copy link
Collaborator

I read the error message, then I take a look to the code.
then compare the prototype of "write"
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiClient.h#L50

size_t write(const uint8_t *buf, size_t size);

with what you try to give in.

uint8_t [64] vs const uint8_t *.
by this i know there only a typecast is needed (uint8_t is compatible to uint8_t).
Its basic C / C++ type conversion.

@rudi48
Copy link
Author

rudi48 commented Dec 10, 2015

Hello Markus,
Thank you very much for the explanation. I have learned something :-)

Unfortunately I looked at line 115 of WiFiClient.h, which was mentioned in the error message:
inline size_t WiFiClient::write(T& source, size_t unitSize) {

The increased buffer size (1460) works also.

Regards, Rudolf

@rudi48
Copy link
Author

rudi48 commented Dec 10, 2015

Hello Markus,
Does it make sense to place this FTP program to the ESP8266WiFi examples?
Regards, Rudolf

@igrr
Copy link
Member

igrr commented Dec 10, 2015

It would be very useful for others to create a library from this example.

@rudi48
Copy link
Author

rudi48 commented Dec 10, 2015

Hello Ivan,
Who can make such a library?
I am more or less a copy&paste programmer ;-)
My final source code of the example file is documented on
http://www.rudiswiki.de/wiki9/WiFiFTPServer

Regards, Rudolf

@rudi48
Copy link
Author

rudi48 commented Dec 10, 2015

I have added an upload / download flag to the program:
http://www.rudiswiki.de/wiki9/WiFiFTPServer

Regards, Rudolf

@igrr igrr changed the title Compiler error at FTP Client program, help needed Add FTP Client library Dec 13, 2015
@danbicks
Copy link

Rudi, must say awesome work. I have tried example with my server it starts all good asks for user
then password which is sent, then just sits there. Any ideas anyone?

Here is serial log:

WiFi connected; IP address: 192.168.0.11
WiFi mode: WIFI_STA
Status: WL_CONNECTED
Ready. Press d, u or r
SPIFFS opened
Command connected
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 8 of 1000 allowed.
220-Local time is now 08:55. Server port: 21.
220-This is a private system - No anonymous login
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
Return DB Ok..
Send USER
331 User SESP@dbtec.org OK. Password required
Return DB Ok..
Send PASSWORD

Thanks Dans

@danbicks
Copy link

PS my server is a just host one, account works fine tested with FTP client.

Dans

@danbicks
Copy link

Rudi Update.

All working, I removed from the client.print lines the F pointer to store string in ram. All works perfect without it on ESP :)

Superb work, finally can FTP files to a server brilliant work. Would be nice to wrap this lot up in a library.

Dans

@rudi48
Copy link
Author

rudi48 commented Feb 15, 2016

Hello Dans,
Thank you very much for finding the F pointer problem. However, from the specification:
http://arduino.esp8266.com/versions/1.6.5-947-g39819f0/doc/reference.html
the Program Memory feature "F" should work.
So, I am not sure that this was the cause of your problem.
Regards, Rudolf

@danbicks
Copy link

Hi Rudi,

Really strange I am using 1.6.5 with latest staging version and your example sketch.

First of all would sit there after sending password and wait 15 mins for FTP server disconnection. I added to your ercv() routine a ten second time-out and tried again, timed out after every attempt so I knew it was just waiting for response from ftp server. Just as a try I removed the F pointer from password and username, tried again and it proceeded further. I then removed all of them from the doFtp routine and hey presto works like a treat.

Maybe Igrr could shed some light on this. I am very happy, have tested it today solidly and works every time.

A big thanks again for all your hard work.

Dans

@rudi48
Copy link
Author

rudi48 commented Feb 15, 2016

Hello Dans,
I believe in what you tested.
I have first tested with Arduino version 1.6.6 and then 1.6.7, but not very intensive.
That may be the difference.
It is very mysterious, but on the other hand the Program Memory feature "F" came from the Atmel Mega chips, where RAM memory is very limited. Fortunately with the ESP8266 it is not so needed.
Regards, Rudolf

@danbicks
Copy link

Hi Rudi,

I have had great success with this and am in the process of wrapping it all up in a library with additional functionality added.

Can you give me a list of all the credits to include within my library. Version history amendments etc.

Big thanks again

Dans

@rudi48
Copy link
Author

rudi48 commented Feb 21, 2016

Hello Dans,
The credit goes to SurferTim, please see the link on top of my listing:
http://playground.arduino.cc/Code/FTP
It was my pleasure, to do something useful for the ESP8266 community.
Thank you very much for providing the program as a library.
Regards, Rudolf

@fendrbendr87
Copy link

Hey rudi48,

I am trying to do something similar but keep running into roadblocks. I am trying to just simply write a "hello" onto a text file on an ftp server. The Arduino with the ESP8266 chip is communicating just fine with the FTP server through the Serial Monitor AT commands, but I am having trouble applying your code. I don't have an SD card reader / writer. Would you be able to help me with developing a code that simply writes "hello" onto a text file on an ftp server? Once I can do that, I can figure out how to transmit the data from multiple sensors onto the ftp server, and access it remotely from another location, which is my main goal.

Any assistance would be greatly appreciated.

Thank you!

-Gene

@rudi48
Copy link
Author

rudi48 commented May 13, 2016

Hello Gene,
I have build end of 2015 a temperature measurement with an ESP8266 Module, with WiFi connection
to a Raspberry Pi as an Web Server. Please have a look at
http://www.rudiswiki.de/wiki9/WiFiLoggerOneWire
This may be an example for your project.
Also http://thingspeak.com/ could be used.
Regards, rudi48

@Clemzo
Copy link

Clemzo commented Jun 9, 2016

..

@akashboghani
Copy link

Hi,
how can I do this while the ESP8266 is connected to the Arduino?

I need the ESP/Arduino setup to act as a client and upload a text file periodically to the ftp server.

@BinarySurfer
Copy link

Hi akashboghani,

Did you solve the problem?

I assume you have the ESP8266 module wired to an Arduino board?

I connect successfully to my FTP server & I am able to manipulate the directories etc

I am also able to get my Dataport but I have no idea how to connect to it.

The ESP8266wifi.h seems to only allow one client connection at a time........I must be missing something very simple!

Thanks

@istabraqmahmood
Copy link

the code is not working, the SPIFFS open fail !
any help, please ?

@rudi48
Copy link
Author

rudi48 commented Nov 3, 2016

@stabraqmahmood
Some important informations are missing:

  1. What ESP8266 module is used?
  2. What Arduino version is used?
  3. What is the error message?

@istabraqmahmood
Copy link

@rudi48
the serial monitor show:
WiFi connected; IP address: 192.168.0.183
WiFi mode: WIFI_STA
Status: WL_CONNECTED
................................
SPIFFS open fail


i`m using nodemcu v 1.0 (esp12-e) on arduino IDE v 1.6.12
please help
thanks

@rudi48
Copy link
Author

rudi48 commented Nov 4, 2016

@stabraqmahmood
It may be that your SPIFFS file system is not yet formatted, please see for details:
arduino-esp8266fs-plugin
you may also have a look at my wiki:
NodeMCU

@MajedRayan
Copy link

Hi rudi48
great work
but can I use SD card instead of SPIFFS
I replace FS.h with SD.h
`
#include <ESP8266WiFi.h>
#include <SPI.h>
#include <SD.h>
// comment out next line to write to SD from FTP server
#define FTPWRITE

// Set these to your desired softAP credentials. They are not configurable at runtime.
const char *ssid = "SSID";
const char *password = "PASSWORD";

//boolean debug = false; // true = more messages
boolean debug = true;
//start using SD
//static bool hasSD = false;

// LED is needed for failure signalling
const short int BUILTIN_LED2 = 16; //GPIO16 on NodeMCU (ESP-12)

unsigned long startTime = millis();

// provide text for the WiFi status
const char *str_status[]= {
"WL_IDLE_STATUS",
"WL_NO_SSID_AVAIL",
"WL_SCAN_COMPLETED",
"WL_CONNECTED",
"WL_CONNECT_FAILED",
"WL_CONNECTION_LOST",
"WL_DISCONNECTED"
};

// provide text for the WiFi mode
const char *str_mode[]= { "WIFI_OFF", "WIFI_STA", "WIFI_AP", "WIFI_AP_STA" };

// change to your server
IPAddress server(185,27,134,11);

WiFiClient client;
WiFiClient dclient;

char outBuf[128];
char outCount;

// change fileName to your file (8.3 format!)
String fileName = "test.txt";
String path = "/test.txt";
// SD file handle
File myFile;

void signalError() { // loop endless with LED blinking in case of error
while(1) {
digitalWrite(BUILTIN_LED2, LOW);
delay(300); // ms
digitalWrite(BUILTIN_LED2, HIGH);
delay(100); // ms
}
}

//format bytes

String formatBytes(size_t bytes) {
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
} else {
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
}
}

//----------------------- WiFi handling
void connectWifi() {
Serial.print("Connecting as wifi client to SSID: ");
Serial.println(ssid);
Serial.println("CONECTED");
// use in case of mode problem
WiFi.disconnect();
// switch to Station mode
if (WiFi.getMode() != WIFI_STA) {
WiFi.mode(WIFI_STA);
}

WiFi.begin ( ssid, password );

if (debug ) WiFi.printDiag(Serial);

// ... Give ESP 10 seconds to connect to station.
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
delay(500);
Serial.print(".");
}
Serial.println("");
// Check connection
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi connected; IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.print("WiFi connect failed to ssid: ");
Serial.println(ssid);
Serial.print("WiFi password <");
Serial.print(password);
Serial.println(">");
Serial.println("Check for wrong typing!");
}
} // connectWiFi()

//----------------- FTP fail
void efail() {
byte thisByte = 0;

client.println(F("QUIT"));

while (!client.available()) delay(1);

while (client.available()) {
thisByte = client.read();
Serial.write(thisByte);
}

client.stop();
Serial.println(F("Command disconnected"));
myFile.close();
Serial.println(F("SD closed"));
} // efail

//-------------- FTP receive
byte eRcv() {
byte respCode;
byte thisByte;

while (!client.available()) delay(1);

respCode = client.peek();

outCount = 0;

while (client.available()) {
thisByte = client.read();
Serial.write(thisByte);

if (outCount < 127) {
  outBuf[outCount] = thisByte;
  outCount++;
  outBuf[outCount] = 0;
}

}

if (respCode >= '4') {
efail();
return 0;
}
return 1;
} // eRcv()

//--------------- FTP handling
byte doFTP(boolean upload) {

if (upload) {
myFile = SD.open(fileName,FILE_READ);
// myFile = SD.open(path, "r");
} else {
SD.remove(path);
myFile = SD.open(fileName,FILE_READ);
// myFile = SD.open(path, "w");
}

if (!myFile) {
Serial.println(F("SD open fail"));
return 0;
}

if (upload) {
if (!myFile.seek(0)) {
Serial.println(F("Rewind fail"));
myFile.close();
return 0;
}
}

if (debug) Serial.println(F("SD opened"));

if (client.connect(server, 21)) { // 21 = FTP server
Serial.println(F("Command connected"));
} else {
myFile.close();
Serial.println(F("Command connection failed"));
return 0;
}

if (!eRcv()) return 0;
if (debug) Serial.println("Send USER");
client.println(F("USER USER"));

if (!eRcv()) return 0;
if (debug) Serial.println("Send PASSWORD");
client.println(F("PASS PASSWORD"));

if (!eRcv()) return 0;
if (debug) Serial.println("Send SYST");
client.println(F("SYST"));

if (!eRcv()) return 0;
if (debug) Serial.println("Send Type I");
client.println(F("Type I"));

if (!eRcv()) return 0;
if (debug) Serial.println("Send PASV");
client.println(F("PASV"));

if (!eRcv()) return 0;

char *tStr = strtok(outBuf, "(,");
int array_pasv[6];
for ( int i = 0; i < 6; i++) {
tStr = strtok(NULL, "(,");
array_pasv[i] = atoi(tStr);
if (tStr == NULL) {
Serial.println(F("Bad PASV Answer"));
}
}
unsigned int hiPort, loPort;
hiPort = array_pasv[4] << 8;
loPort = array_pasv[5] & 255;

if (debug) Serial.print(F("Data port: "));
hiPort = hiPort | loPort;
if (debug) Serial.println(hiPort);

if (dclient.connect(server, hiPort)) {
Serial.println(F("Data connected"));
}
else {
Serial.println(F("Data connection failed"));
client.stop();
myFile.close();
return 0;
}

if (upload) {
if (debug) Serial.println("Send STOR filename");
client.print(F("STOR "));
client.println(fileName);
} else {
if (debug) Serial.println("Send RETR filename");
client.print(F("RETR "));
client.println(fileName);
}

if (!eRcv()) {
dclient.stop();
return 0;
}

if (upload) {
if (debug) Serial.println(F("Writing"));
// for faster upload increase buffer size to 1460
//#define bufSizeFTP 64
#define bufSizeFTP 1460
uint8_t clientBuf[bufSizeFTP];
//unsigned int clientCount = 0;
size_t clientCount = 0;

while (myFile.available()) {
  clientBuf[clientCount] = myFile.read();
  clientCount++;
  if (clientCount > (bufSizeFTP - 1)) {
    dclient.write((const uint8_t *) &clientBuf[0], bufSizeFTP);
    clientCount = 0;
    delay(1);
  }
}
if (clientCount > 0) dclient.write((const uint8_t *) &clientBuf[0], clientCount);

} else {
while (dclient.connected()) {
while (dclient.available()) {
char c = dclient.read();
myFile.write(c);
if (debug) Serial.write(c);
}
}
}

dclient.stop();
Serial.println(F("Data disconnected"));

if (!eRcv()) return 0;

client.println(F("QUIT"));

if (!eRcv()) return 0;

client.stop();
Serial.println(F("Command disconnected"));

myFile.close();
if (debug) Serial.println(F("SPIFS closed"));
return 1;
} // doFTP()

void readSD()
{
myFile = SD.open(fileName,FILE_READ);

if(!myFile)
{
Serial.println(F("SD open fail"));
return;
}

while(myFile.available())
{
Serial.write(myFile.read());
}

myFile.close();
}// read SD card
void setup() {
delay(1000);
Serial.begin(115200);
delay(1000);
Serial.println("Sync,Sync,Sync,Sync,Sync");
delay(500);
Serial.println();
// signal start
pinMode(BUILTIN_LED2, OUTPUT);
digitalWrite(BUILTIN_LED2, LOW);
delay(400); // ms
digitalWrite(BUILTIN_LED2, HIGH);
delay(400); // ms

Serial.print("Chip ID: 0x");
Serial.println(ESP.getChipId(), HEX);

Serial.println ( "Connect to Router requested" );
connectWifi();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi mode: ");
Serial.println(str_mode[WiFi.getMode()]);
Serial.print ( "Status: " );
Serial.println (str_status[WiFi.status()]);
// signal WiFi connect
digitalWrite(BUILTIN_LED2, LOW);
delay(300); // ms
digitalWrite(BUILTIN_LED2, HIGH);
} else {
Serial.println("");
Serial.println("WiFi connect failed, push RESET button.");
signalError();
}
Serial.print("Initializing SD card...");

if (!SD.begin(4) == 0) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
if (SD.exists("test.txt")) {
Serial.println("test.txt exists.");
}
else {
Serial.println("test.txt doesn't exist.");
}
Serial.println("Creating example.txt...");
myFile = SD.open("example.txt", FILE_WRITE);
myFile.close();

// Check to see if the file exists:

/*
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
}
else {
Serial.println("example.txt doesn't exist.");
}
*/
Serial.println(F("Ready. Press d, u or r"));
} // setup()

void loop() {
byte inChar;

if (Serial.available() > 0) {
inChar = Serial.read();
}

boolean upload = true; // false = download

if (inChar == 'd') {
upload = false;
if (doFTP(upload)) Serial.println(F("FTP OK"));
else Serial.println(F("FTP FAIL"));
}

if (inChar == 'u') {
upload = true;
if (doFTP(upload)) Serial.println(F("FTP OK"));
else Serial.println(F("FTP FAIL"));
}

if (inChar == 'r') {
readSD();
}
delay(10); // give time to the WiFi handling in the background
}

`

I can't compile the code it give me error
`
C:\Users\Black\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libraries\SD\src/SD.h:68:5: error: request for member 'read' in 'src', which is of non-class type 'char'

 src.read(obuf, leftLen);

`

@cow77
Copy link

cow77 commented Nov 14, 2016

If in doFTP you change:
while (dclient.connected()) {
while (dclient.available()) {
char c = dclient.read();
myFile.write(c);
if (debug) Serial.write(c);
}
}

to

while (dclient.connected()) {
  while (dclient.available()) {
    byte c = dclient.read();
    myFile.write(c);
    if (debug) Serial.write(c);
  }
}

(char changed to byte), at least it compiles error-free.

@rudi48
Copy link
Author

rudi48 commented Oct 19, 2017

No, I do not have the expertise to do so.

@devyte devyte added level: easy and removed waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. labels Oct 19, 2017
@TomElectronic
Copy link

Hello rudi or everybody who can help.
I am writing source code for ESP8266 with Kinetis MCU.
I don't use Arduino library, I am writing all low level code.

Wifi is working fine. I added FTP client functionality, it works fine (I can connect to Filezilla Server) but I can't send "data" of my ASCII file.
I understand with wireshark and your code that after STOR command, I must read new port number and send the content of my file to this port.
But I don't know how to change this port because I am already connected at port 21 (with AT+CIPSTART="TCP","192.168.0.1",21\r\n").
And visibly, when ESP8266 is connected to one port, he refused to connect to another port without disconnect before.

I supposed that I would create 2 FTP clients (as your code with "client" and "dclient") but it don't work.
I tried :
AT+CIPMUX=1\r\n; // multiple connections allowed
OK
AT+CIPSTART=0,"TCP","192.128.0.1",21\r\n // create Client0 and connect to server on port 21
Link type ERROR

Do you have an idea to help me, pls ?
Regards,
Thomas.

@devyte
Copy link
Collaborator

devyte commented Feb 28, 2018

@TomElectronic you're in the wrong place, you're talking about the AT firmware, but this is the Arduino firmware.

@TomElectronic
Copy link

Yes, you're right, I'm sorry...

But this is the only code that I found who use 2 client with ESP8266.
And I don't understand how to do that at AT command level.

I just wrote a message on espressif forum that I found after that post.
I hope someone read this and know AT programming for this module so he can give me advices...

Thanks,
Thomas.

@Skm1982
Copy link

Skm1982 commented Mar 10, 2018

Hello everybody. Is there a way to realize a secure ftp connection with esp8266 and this client? I mean FTPS one. I used WiFiClientSecure to open control socket and plain WiFiClient for data - it works fine. Unfortunately client can not make secure connection on data. And as far as I understand it is not 'lack of memory' problem - I tried to open secure control connections on two independent servers, worked too. Is it possible to make data connection secured too? Thanks.

@topnotchrally
Copy link

I have this code working on my ESP8255. It will connect and transfer a file to my Mac but will not do so to my Raspberry Pi, with ProFTPD server. I can connect/transfer to the RPi using Filezilla.

Can someone please help. I wish to FTP from the ESP to the RPi.
Here is what I see on the ESP

WiFi connected; IP address: 192.168.2.100
WiFi mode: WIFI_STA
Status: WL_CONNECTED
Ready. Press d, u or r
SPIFFS opened
Command connected
220 ProFTPD 1.3.5 Server (Debian) [::ffff:192.168.2.148]
Send USER
331 Password required for pi
Send PASSWORD
230 User pi logged in
Send SYST
215 UNIX Type: L8
Send Type I
200 Type set to I
Send PASV
227 Entering Passive Mode (192,168,2,148,176,83).
Data port: 45139
Data connection failed
FTP FAIL

Thanks.

@NightSkySK
Copy link

I have had great success with this and am in the process of wrapping it all up in a library with additional functionality added.

Dans, Have you converted this into Library? It would help us a lot!

@devyte
Copy link
Collaborator

devyte commented Dec 18, 2019

Reducing 2.7.0 scope, pushing back

@dplasa
Copy link

dplasa commented May 26, 2020

I have had great success with this and am in the process of wrapping it all up in a library with additional functionality added.

Dans, Have you converted this into Library? It would help us a lot!

I have worked on that code base a little bit and put together a working library that features

  • LittleFS (including directory support) / SPIFFS support
  • active and passive mode FTP
  • supports ftp FEAT, SIZE, MSLD, MDTM commands
  • works nicely with ftp command or FileZilla

Maybe you want to give this a try and check it out. See https://github.com/dplasa/esp8266FTPServer

@danbicks
Copy link

danbicks commented May 27, 2020 via email

@dplasa
Copy link

dplasa commented May 27, 2020

Hi Dans,
thanks for sharing!
Now that I've read your code, I notice my sloppy reading of this thread... wile this is all about an ftp client, my library provides a ftp server ...
:)

@danbicks
Copy link

danbicks commented May 28, 2020 via email

@dplasa
Copy link

dplasa commented May 28, 2020

Hey Dans, thanks!
Well, speed is about 180kB/s FROM and 40kB/s TO the esp8266. That is not wowing but I actually just use it to put configuration files on my projects ;-)

After reading your code, that I realized, I could include a client with almost no sweat.... So I took the liberty, to include a ftp client based on your code as well.

See https://github.com/dplasa/FTPClientServer !

@devyte
Copy link
Collaborator

devyte commented May 29, 2020

Nice work. Now that there's both a client and server, I suggest the following:

  • Add a comment in your repo Readme stating the core version against which you tested
  • Be more explicit about credits, i. e. mention danbicks explicitly in addition to linking to this thread
  • Add some kind of CI or job or test to check building against the core. There are some changes coming, and some could be breaking. It would be good to have some automated test to check at least that thw code compiles.

I have some comments about code cleanup and good practices, but we can discuss that in your repo.
To leave the comment here, I suggest factoring out the WiFiClient/Server so that they can be provided as constructor arguments. This is a precursor to supporting encryption, aka FTPS. The setup for CA etc would be done outside before passing the objects in, see examples.

Finally, I'll add a link to your repo and close this.

dplasa added a commit to dplasa/FTPClientServer that referenced this issue May 30, 2020
@JeffZuckerberg
Copy link

I have had great success with this and am in the process of wrapping it all up in a library with additional functionality added.

Dans, Have you converted this into Library? It would help us a lot!

I have worked on that code base a little bit and put together a working library that features

  • LittleFS (including directory support) / SPIFFS support
  • active and passive mode FTP
  • supports ftp FEAT, SIZE, MSLD, MDTM commands
  • works nicely with ftp command or FileZilla

Maybe you want to give this a try and check it out. See https://github.com/dplasa/esp8266FTPServer

I dplasa,

i've tried to use your library, but i got this error every time and i can't transfert a file to FTP server : Transfer failed after 1 ms, code: 4294967295, descr=Local file error.
Even with the example you provide, i got the same error.

Could you help me to manage it please ? I've attached a screenshot of the error message.

Best regards,
error dplasa

@dplasa
Copy link

dplasa commented Jan 3, 2022 via email

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

Successfully merging a pull request may close this issue.