Receiving exception 28 while uploading data to a remote server via FTP using ESP8266

Hello!!

Project Info : I am uploading a .csv file to a remote FTP server using ESP8266 nodemcu as a FTP client.I am using LittleFS file system to make a file, append it and then send it to the remote FTP server.I am able to upload the file to the server but sometimes ESP8266 restarts and write garbage data to it (Note : before it restarts, it throws an exception (28) )

This is my code :

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#include <Arduino.h>


/*------------------------------------------------------
     LIBRARIES
 --------------------------------------------------------*/
 
#include<LittleFS.h>
#include <ESP8266WiFi.h>


//FTP server details

/*
char* xhost = ; //synology diskstation
char* xusername = ;
char* xpassword = ;
char* xfolder = ; 
*/


/*destination folder on FTP server
this is an optional parameter with the function prototype 
defined below. To leave the default folder on the server 
unchanged, either enter "" for xfolder or omit this param in 
the function call in doFTP() */

//file to upload

char* xfilename = "file.csv"; 

short FTPresult; //outcome of FTP upload

/*------------------------------------------------------
 * OBJECTS
 --------------------------------------------------------*/
/*------------------------------------------------------
     SETUP
 --------------------------------------------------------*/

//Function prototype - required if folder is an optional argument
short doFTP(char* , char* , char* , char* , char* = "");
 
void setup()
 {
  //a one-time test to check it works

  Serial.begin(9600);
  delay(100);


//Wifi Setup
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println("Prima");
  WiFi.begin("Prima","Prima1234");

  while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print("."); 
    }

  Serial.println();
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("----------------------------------------");


//----------------------------------------------------

// LittleFS setup

  LittleFS.begin();

// assumes that LittleFS exists and is formatted

//Open test file, write sample data to it
  File f = LittleFS.open(xfilename, "a");
  if (!f) 
  {
    Serial.println("file open failed");
    Serial.println("Sketch terminating..."); 
  }
  else 
  {
    Serial.println("file data.txt exists in LittleFS");
    //write sample data
    f.println("Hello");
    f.close();
    //LittleFS.available()

    //get and print contents of root folder
    String str = "";
    Dir dir = LittleFS.openDir("");
    while (dir.next()) {
      str += dir.fileName();
      str += " / ";
      str += dir.fileSize();
      str += "\r\n";}
    Serial.print(str);
    Serial.println("--------------------------------");

 //Attempt FTP upload

    FTPresult = doFTP("65.2.20.165","primaftpuser","Primaftp@123",xfilename,"/FTPTest");
    //What is the outcome?
    Serial.println("A return code of 226 is success");
    Serial.print("Return code = ");
    Serial.println(FTPresult);

    }
    
} //end setup

/*
  ------------------------------------------------------
 * MAIN LOOP
 -------------------------------------------------------- */

void loop() 
{

  LittleFS.begin();
  delay(200);

  File f = LittleFS.open(xfilename, "a");

  if (!f) 
     {
        Serial.println("file open failed");
        Serial.println("Sketch terminating...");
     }
  else 
    {
        Serial.println("file exists in LittleFS");

        f.println("123");
        
        delay(50);  
        f.close();
        delay(200);
        
        FTPresult = doFTP("<FTP server IP>","<user name>","<password>",xfilename,"/FTPTest");
    }
    
    delay(5000);


 /* char* x1filename = "1_10.csv"; */
/*
  LittleFS.remove(x1filename);
  delay(100);

*/

}

/*------------------------------------------------------

 * FUNCTION - doFTP
 * Connects to a FTP server and transfers a file. Connection
 * is established using the FTP commands/responses defined at
 * https://en.wikipedia.org/wiki/List_of_FTP_commands or in
 * more detail at http://www.nsftools.com/tips/RawFTP.htm
 * 
 * Parameters passed:
 *   host - the IP address of the FTP server
 *   uname - username for the account on the server
 *   pwd - user password
 *   filename - the file to be transferred
 *   folder (optional) - folder on the server where 
 *   the file will be copied. This is may be omitted if 
 *   the file is to be copied into the default folder on
 *   the server.
 *   
 * Return codes:  
 *    226 - a successful transfer
 *    400+ - any return code greater than 400 indicates
 *    an error. These codes are defined at
 *    https://en.wikipedia.org/wiki/List_of_FTP_server_return_codes 
 *    Exceptions to this are:
 *    900 - failed to open file on LittleFS
 *    910 - failed to connect to server
 *    
 * Dependencies:
 *   Libraries - <ESP8266WiFi.h> wifi library
 *               <FS.h> LittleFS library
 *   Functions - eRcv
 --------------------------------------------------------*/

 /*-------------------------------- -------------------*/ 
 
short eRcv(WiFiClient aclient, char outBuf[], int size)
{
  byte thisByte;
  char index;
  String respStr = "";
  while(!aclient.available()) delay(1);
  index = 0;
  while(aclient.available()) 
  {  
    thisByte = aclient.read();    
    Serial.write(thisByte);
    if(index < (size - 2)) { //less 2 to leave room for null at end
      outBuf[index] = thisByte;
      index++;}
  } //note if return from server is > size it is truncated.
  outBuf[index] = 0; //putting a null because later strtok requires a null-delimited string
  //The first three bytes of outBuf contain the FTP server return code - convert to int.
  for(index = 0; index < 3; index++) {respStr += (char)outBuf[index];}
  return respStr.toInt();
} // end function eRcv


short doFTP(char* host, char* uname, char* pwd, char* fileName, char* folder)
{
  WiFiClient ftpclient;
  WiFiClient ftpdclient;

  const short FTPerrcode = 400; //error codes are > 400
  const byte Bufsize = 128;
  char outBuf[Bufsize];
  short FTPretcode = 0;
  const byte port = 21; //21 is the standard connection port
  
  File ftx = LittleFS.open(fileName, "r"); //file to be transmitted
  if (!ftx) 
  {
    Serial.println(F("file open failed"));
    return 900;
  }

  if (ftpclient.connect(host,port)) 
  {
    Serial.println(F("Connected to FTP server"));
  } 
  else 
  {
    ftx.close();
    Serial.println(F("Failed to connect to FTP server"));
    return 910;
  }
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;
  
    

    
  /* User - Authentication username 
   * Send this command to begin the login process. username should be a 
   * valid username on the system, or "anonymous" to initiate an anonymous login.
   */

//------------------------ ------------------------------------------

  
  ftpclient.print("USER ");
  ftpclient.println(uname);
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;
  
  
  /* PASS - Authentication password
   * After sending the USER command, send this command to complete 
   * the login process. (Note, however, that an ACCT command may have to be 
   * used on some systems, not needed with synology diskstation)
   */
//------------------------ ------------------------------------------
   
  ftpclient.print("PASS ");
  ftpclient.println(pwd);  
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;

  //CWD - Change the working folder on the FTP server
  if(!(folder == "")) {
    ftpclient.print("CWD ");
    ftpclient.println(folder);
    FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
    if(FTPretcode >= 400) {return FTPretcode;} }
  


  /* SYST - Returns a word identifying the system, the word "Type:", 
   * and the default transfer type (as would be set by the 
   * TYPE command). For example: UNIX Type: L8 - this is what
   * the diskstation returns
   */

//------------------------ ------------------------------------------
   
  ftpclient.println("SYST");
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;
  

  
  /* TYPE - sets the transfer mode
   * A - ASCII text
   * E - EBCDIC text
   * I - image (binary data)
   * L - local format
   * for A & E, second char is:
   * N - Non-print (not destined for printing). This is the default if 
   * second-type-character is omitted
   * Telnet format control (<CR>, <FF>, etc.)
   * C - ASA Carriage Control
   */

//------------------------ ------------------------------------------

  
  ftpclient.println("Type I");
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;
  
  /* PASV - Enter passive mode
   * Tells the server to enter "passive mode". In passive mode, the server 
   * will wait for the client to establish a connection with it rather than 
   * attempting to connect to a client-specified port. The server will 
   * respond with the address of the port it is listening on, with a message like:
   * 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2), e.g. from diskstation
   * Entering Passive Mode (192,168,0,5,217,101)
   */
   
 //------------------------ ------------------------------------------
 
  ftpclient.println("PASV");
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) return FTPretcode;

  /* This is parsing the return from the server
   * where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number. 
   */

  char *tStr = strtok(outBuf,"(,"); //chop the output buffer into tokens based on the delimiters
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) { //there are 6 elements in the address to decode
    tStr = strtok(NULL,"(,"); //1st time in loop 1st token, 2nd time 2nd token, etc.
    array_pasv[i] = atoi(tStr); //convert to int, why atoi - because it ignores any non-numeric chars
                                //after the number
    if(tStr == NULL) {Serial.println(F("Bad PASV Answer"));}
  }

  //extract data port number

  unsigned int hiPort,loPort;
  hiPort=array_pasv[4]<<8; //bit shift left by 8
  loPort=array_pasv[5]&255;//bitwise AND
  Serial.print(F("Data port: "));
  hiPort = hiPort|loPort; //bitwise OR
  Serial.println(hiPort);
  //first instance of dftp
  if(ftpdclient.connect(host, hiPort))
  {
    Serial.println(F("Data port connected"));
  }
  else 
  {
    Serial.println(F("Data connection failed"));
    ftpclient.stop();
    ftx.close(); 
  }

  /* STOR - Begin transmission of a file to the remote site. Must be preceded 
   * by either a PORT command or a PASV command so the server knows where 
   * to accept data from
   */

  ftpclient.print("STOR ");
  ftpclient.println(fileName);
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) {
    ftpdclient.stop();
    return FTPretcode; } 
  Serial.println(F("Writing..."));
  
  byte clientBuf[64];
  int clientCount = 0;
  
  while(ftx.available()) 
  {
    clientBuf[clientCount] = ftx.read();
    clientCount++;
    
    if(clientCount > 63) 
    {
      ftpdclient.write((const uint8_t *)clientBuf, 64);
      clientCount = 0; 
    }
  }
  if(clientCount > 0) ftpdclient.write((const uint8_t *)clientBuf, clientCount);
  ftpdclient.stop();
  Serial.println(F("Data disconnected"));
  FTPretcode = eRcv(ftpclient,outBuf,Bufsize);
  if(FTPretcode >= 400) {return FTPretcode; } 
  
  //End the connection
  ftpclient.println("QUIT");
  ftpclient.stop();
  Serial.println(F("Disconnected from FTP server"));

  ftx.close();
  Serial.println(F("File closed"));
  return FTPretcode;
} // end function doFTP


/*------------------------------------------------------
 * FUNCTION - eRcv
 * Reads the response from an FTP server and stores the 
 * output in a buffer.Extracts the server return code from
 * the buffer.
 * 
 * Parameters passed:
 *   aclient - a wifi client connected to FTP server and
 *   delivering the server response
 *   outBuf - a buffer to store the server response on
 *   size - size of the buffer in bytes
 *   
 * Return codes:  
 *    These are the first three chars in the buffer and are 
 *    defined in 
 *    https://en.wikipedia.org/wiki/List_of_FTP_server_return_codes 
 *    
 * Dependencies:
 *   Libraries - <ESP8266WiFi.h> wifi library
 *   Functions - none
 --------------------------------------------------------*/


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

This is the error i am receiving :

15:02:34.793 ->
15:02:34.793 -> Exception (28):
15:02:34.793 -> epc1=0x402107ae epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
15:02:34.888 ->
15:02:34.888 -> >>>stack>>>
15:02:34.935 ->
15:02:34.935 -> ctx: cont
15:02:34.935 -> sp: 3ffffbc0 end: 3fffffc0 offset: 0190
15:02:34.982 -> 3ffffd50: 0000007e 00000003 3ffffdc0 4020e138
15:02:35.028 -> 3ffffd60: 00000030 3ffe8368 3ffef67c 402091c0
15:02:35.075 -> 3ffffd70: 40207b8c 3ffffe40 000000c8 40205482
15:02:35.122 -> 3ffffd80: 00000000 3ffe8905 00000000 3fffff0c
15:02:35.169 -> 3ffffd90: 0000018f 3fffff24 3ffee854 40210910
15:02:35.216 -> 3ffffda0: 3fffdad0 3ffe85c8 3ffee854 4020e110
15:02:35.311 -> 3ffffdb0: 0000018f 3fffff24 3ffee854 40205740
15:02:35.357 -> 3ffffdc0: 20303032 65707954 74657320 206f7420
15:02:35.404 -> 3ffffdd0: 000a0d49 6c694620 6c695a65 0a0d616c
15:02:35.451 -> 3ffffde0: 75632000 6e657272 69642074 74636572
15:02:35.498 -> 3ffffdf0: 2e79726f 67000a0d 46207265 40005054
15:02:35.545 -> 3ffffe00: 00000000 00000000 00000000 00000000
15:02:35.592 -> 3ffffe10: 00000000 40204518 3ffffe6c 00000000
15:02:35.639 -> 3ffffe20: 00000001 00001fc0 500ffc30 ffffffff
15:02:35.685 -> 3ffffe30: 00001f80 00001ff8 500ffc30 ffffffff
15:02:35.779 -> 3ffffe40: 4020cdc4 00000000 00001388 00001f80
15:02:35.863 -> 3ffffe50: 00000000 00000000 3ffefeec 00000000
15:02:35.873 -> 3ffffe60: 00000000 00000000 00000000 3ffef57c
15:02:35.920 -> 3ffffe70: 3ffffe20 3fff03c4 00000008 3ffef57c
15:02:35.966 -> 3ffffe80: 4020ce40 00000000 000003e8 3ffef5f4
15:02:36.013 -> 3ffffe90: 00000000 3ffefddc 3ffefdcc 40206470
15:02:36.060 -> 3ffffea0: 00000000 00000000 3ffee80c 401006bf
15:02:36.107 -> 3ffffeb0: 37400008 3fffff30 37400000 3ffee8e0
15:02:36.153 -> 3ffffec0: 3fffdad0 00000020 3fff0314 4020cdc4
15:02:36.246 -> 3ffffed0: 00000000 00001388 3ffe85e0 00000000
15:02:36.293 -> 3ffffee0: 3ffffeec 00000000 00000000 4020cdc4
15:02:36.340 -> 3ffffef0: 00000000 00001388 3ffefdcc 00000000
15:02:36.387 -> 3fffff00: 00000000 3ffefeec 00000000 40213ce8
15:02:36.433 -> 3fffff10: 007a1200 dbcac153 3ffefd00 40213c74
15:02:36.480 -> 3fffff20: 401059fd 08d74734 3ffee944 00000000
15:02:36.526 -> 3fffff30: 3ffe886a 3ffe8937 3ffe8847 3ffe885d
15:02:36.573 -> 3fffff40: 3ffe8850 00000000 000000c8 40209368
15:02:36.620 -> 3fffff50: 40209a25 000000c8 3ffee8e0 3ffee8e0
15:02:36.714 -> 3fffff60: 3fffdad0 3ffe85c8 3ffee854 40205983
15:02:36.761 -> 3fffff70: 4020ce40 00000000 000003e8 00000000
15:02:36.808 -> 3fffff80: 00000000 00000000 00000000 40206470
15:02:36.883 -> 3fffff90: 00000000 00000000 3ffee80c 40100185
15:02:36.883 -> 3fffffa0: 3fffdad0 00000000 3ffee8cc 40209488
15:02:36.948 -> 3fffffb0: feefeffe feefeffe 3ffe85dc 40100b5d
15:02:36.994 -> <<<stack<<<

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

** This is the decoded version of exception i am receiving (for which i have used ESP Exception Decoder tool ) :**

Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads

PC: 0x402107ae

EXCVADDR: 0x00000000

Decoding stack results

0x4020e138: atol at /workdir/repo/newlib/newlib/libc/stdlib/atol.c line 12

0x402091c0: String::toInt() const at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/WString.h line 277

0x40207b8c: WiFiClient::write(unsigned char const, unsigned int)* at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src*WiFiClient.cpp* line 213

0x40205482: eRcv(WiFiClient, char, int)* at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/WString.h line 79

0x40210910: strtol at /workdir/repo/newlib/newlib/libc/stdlib/../locale/setlocale.h line 237

0x4020e110: atoi at /workdir/repo/newlib/newlib/libc/stdlib/atoi.c line 52

0x40205740: doFTP(char, char, char*, char*, char*)** at C:\Users\PE-COM-14\Desktop\Hi_tech_backup\FTP1/FTP1.ino line 343

0x40204518: lfs_dir_commit_commit at c:\users\pe-com-14\appdata\local\arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\littlefs\lib\littlefs/lfs.c line 1560

0x40206470: fs::FS::_defaultTimeCB() at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h line 249

0x401006bf: umm_free_core(umm_heap_context_t, void)** at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266\umm_malloc*umm_malloc.cpp* line 549

0x40213ce8: operator delete(void)* at /workdir/repo/gcc-gnu/libstdc++-v3/libsupc++/del_op.cc line 49

0x40213c74: operator delete(void, unsigned int)* at /workdir/repo/gcc-gnu/libstdc++-v3/libsupc++/del_ops.cc line 33

0x40209368: __esp_yield() at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266*core_esp8266_main.cpp* line 116

0x40209a25: __delay(unsigned long) at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266*core_esp8266_wiring.cpp* line 55

0x40205983: loop() at C:\Users\PE-COM-14\Desktop\Hi_tech_backup\FTP1/FTP1.ino line 147

0x40206470: fs::FS::_defaultTimeCB() at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h line 249

0x40100185: esp_schedule() at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266*core_esp8266_main.cpp* line 128

0x40209488: loop_wrapper() at C:\Users\PE-COM-14\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266*core_esp8266_main.cpp* line 201

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

What could be the issue triggering exception??

Basically it informs you that it is reading from or writing an address or pointer that is no longer in scope.
I don't know if it's relevant, but you only need to call the constructor once, so remove this at the beginning of loop()

  LittleFS.begin();
  delay(200);

But i actually suspect it is the 'strtok() bug' I thought it had been repaired, and it may actually be through some misuse on your account.

char *tStr = strtok(outBuf,"(,"); //chop the output buffer into tokens based on the delimiters
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) { //there are 6 elements in the address to decode
    tStr = strtok(NULL,"(,"); //1st time in loop 1st token, 2nd time 2nd token, etc.
    array_pasv[i] = atoi(tStr); //convert to int, why atoi - because it ignores any non-numeric chars
                                //after the number
    if(tStr == NULL) {Serial.println(F("Bad PASV Answer"));}
  }

you see you create a char * named 'tStr' with an undefined size, now that pointer will take up space on the stack for it's size. But then you assign it a new value. It should be OK, but it may not be, and this test

if(tStr == NULL) {Serial.println(F("Bad PASV Answer"));}

should for sure be done before

array_pasv[i] = atoi(tStr);

and prevent the execution of that line.
These c-string functions tend to cause some issues in the ESP8266 core (or at least they used to) particularly strtok().
I tend to avoid using them on an esp8266 because of that.

I am pretty sure it is the parsing function that is causing the issue here, but how about you start with changing it to

char *t = strtok(outBuf,"(,"); //chop the output buffer into tokens based on the delimiters
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) { //there are 6 elements in the address to decode
    char *tStr = strtok(NULL,"(,"); //1st time in loop 1st token, 2nd time 2nd token, etc.
    if(tStr == NULL) {Serial.println(F("Bad PASV Answer"));}
    else {
    array_pasv[i] = atoi(tStr); //convert to int, why atoi - because it ignores any non-numeric chars
                                //after the number
   }
  }

May we ask for the serial output you got before the exception?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.