[Solved] Udp.write() and tcp.write() 20 second hang

this is the version without using the SD, it seems it’s still causing the same odd behaviour:


#define BUF_SIZE 1024

#define TIME_DELAY 1000
// *** JUST FILL IN THIS DATA TO TEST ***
#define FTP_USER "user"
#define FTP_PASS "pass"
#define FTP_ADDRESS "148.251.48.69" 
#define FTP_DATA_ADDRESS "148,251,48,69," 
#define FILE_TO_UPLOAD "DSCF0003.jpg"
// **************************************

// SOFTWARE SPI pin configuration - modify as required
// The default pins are the same as HARDWARE SPI
const uint8_t chipSelect = A2;    // Also used for HARDWARE SPI setup
const uint8_t mosiPin = A5;
const uint8_t misoPin = A4;
const uint8_t clockPin = A3;

TCPClient ClientOperation,ClientData;
String messageToSend,messageReceived;

char LineBreaker[2];
int step=-1;
byte serverAddress[4];
  

int StartingPosition;
int EndingPosition;
int value;
String preString=FTP_DATA_ADDRESS;
String RawValue;
int length;

int totalread=0;
int fileSize=0;

void error(const char* str)
{

}


void ipArrayFromString(byte ipArray[], String ipString) {
  int dot1 = ipString.indexOf('.');
  ipArray[0] = ipString.substring(0, dot1).toInt();
  int dot2 = ipString.indexOf('.', dot1 + 1);
  ipArray[1] = ipString.substring(dot1 + 1, dot2).toInt();
  dot1 = ipString.indexOf('.', dot2 + 1);
  ipArray[2] = ipString.substring(dot2 + 1, dot1).toInt();
  ipArray[3] = ipString.substring(dot1 + 1).toInt();
}

void connectToMyServer(String ip) {

  ipArrayFromString(serverAddress, ip);
  if (ClientOperation.connect(serverAddress, 21)){// && client1.connect(serverAddress, 9000) && client2.connect(serverAddress, 9000)) {
    Serial.println("connected to the FTP server");
  } else {
    Serial.println("failed ");
  }
}

void setup() {
    Serial.begin(115200);
      
      LineBreaker[0]='\015';
      LineBreaker[1]='\012';
      LineBreaker[2]='\0';

  while (!Serial.available()) Spark.process(); //Spark.process() it seems without the spark has some problem
  
  //just to consume whatever is passed in the begin to start
  while (Serial.available()) Serial.read();


 connectToMyServer(FTP_ADDRESS);
}

void SendFileContent(char * Filename){

    double currentTime,sentFTPTime;
    int remainingBytes=0;
    uint32_t t;
    double r;
    uint32_t n;
    byte * buf;
    
    
  totalread=0;
  fileSize=2411660;

  //allocate space to store a piece of data
  buf = (byte *)malloc(sizeof(byte)*BUF_SIZE); 
  
  n = fileSize/BUF_SIZE; //chunks to send
  t = millis();
  
  Serial.print("n: ");
  Serial.println(n);
  delay(TIME_DELAY);
  
  
  for (uint32_t i = 0; i < (BUF_SIZE); i++) {
    buf[i] = 'A' + (i % 26);
  }
  //send chunks of data, size is BUF_SIZE
  for (uint32_t i = 0; i < n; i++) {
        //The time interval between two TCP/IP actions must >1 ms.
        while((currentTime-sentFTPTime)< 50) 
            currentTime=millis();
        //Serial.println(buf);
        if(ClientData.write(buf,BUF_SIZE)!=BUF_SIZE){
          Serial.println("DID NOT WRITE EVERYTHING!");
          delay(5000);
        }
            
        
        progressReport(BUF_SIZE);
        sentFTPTime=currentTime;
        currentTime=millis();
    //}
        
  }
  free(buf);
  
  
  //at the end this write the remaining bytes if there are any
  remainingBytes=fileSize % BUF_SIZE;
  
  if (remainingBytes>0){
    //allocate space to store a piece of data
    buf = (byte *)malloc(sizeof(byte)*remainingBytes); 
      for (uint32_t i = 0; i < (remainingBytes); i++) {
        buf[i] = 'A' + (i % 26);
      }
        ClientData.write(buf,remainingBytes);
        progressReport(remainingBytes);
    
  }
  free(buf);  
  
  //speed report
  t = millis() - t;
  r = (double)fileSize/t;
  Serial.print("Read ");
  Serial.print(r);
  Serial.println(" kB/sec");
  Serial.println("Done");
    
    
} 

void progressReport(int Add){
    totalread+=Add;
    Serial.print(totalread);
    Serial.print("/");
    Serial.println(fileSize);
    
}


void loop() {

  switch(step){//Prepare the message to send
      case 1:
            messageToSend="USER ";
            messageToSend.concat(FTP_USER);
            delay(TIME_DELAY);
      break;
      case 2:
            messageToSend="PASS ";
            messageToSend.concat(FTP_PASS);
            delay(TIME_DELAY);
      break;      
      case 3:
            messageToSend="PASV";
            delay(TIME_DELAY);
      break;      
      case 4:
            messageToSend="STOR ";
            messageToSend.concat(FILE_TO_UPLOAD);
            delay(TIME_DELAY);
      break; 
      case 5:
           ClientOperation.stop();
           Serial.println("ClientOperation stopped");
           step+=1;
      break; 
  } 
  
  if (messageToSend != ""){//Send the message to the server

    length=messageToSend.length();

    ClientOperation.write((uint8_t *)messageToSend.c_str(),length);
    ClientOperation.write((uint8_t *)LineBreaker,2);
      
    Serial.println(messageToSend);
    messageToSend="";
    delay(3000);
  }
  
  //Client operation   
  if (ClientOperation.connected()) {
   
    while(ClientOperation.available()) {//shows reponses messages from the FTP server
        char charac1 = ClientOperation.read();
        Serial.print(charac1);
        messageReceived+=charac1;
    }
    
    if (step==3){//find the last two number that will determine the port to connect to (from the response message "227 Entering Passive Mode (xx,xx,xx,xx,142,73)")
        length=preString.length();
        StartingPosition=messageReceived.indexOf(preString)+length;
        EndingPosition=messageReceived.indexOf(",",StartingPosition);
        value=messageReceived.substring(StartingPosition,EndingPosition).toInt();
        value*=256;
       
        StartingPosition=EndingPosition+1;
        EndingPosition=messageReceived.indexOf(")",StartingPosition);
        value+=messageReceived.substring(StartingPosition,EndingPosition).toInt();
        ClientData.connect(serverAddress, value); //open a connection and wait to transfer the file
        delay(TIME_DELAY);
    }
    
    if (ClientData.connected() && step==4) { 
      Serial.println("ClientData IS COONECTED: ");
      SendFileContent(FILE_TO_UPLOAD);
      Serial.println("COPYING DONE");
      ClientData.stop();
      Serial.println("CONNECTION CLOSED");
    }
    
    step+=1;
    messageReceived="";
    delay(TIME_DELAY);
  }
  
  
}

i also wrote this to check if somehow the reading of the file from SD would make problems but, it produces a perfect copy of the file without trouble:

/*
 * This sketch is a simple write/read benchmark.
 */
#include "application.h"
#include "sd-card-library/sd-fat.h"
#include "sd-card-library/sd-fat-util.h"

#define FILE_SIZE_MB 5
#define FILE_SIZE (1000000UL*FILE_SIZE_MB)
#define BUF_SIZE 100

// SOFTWARE SPI pin configuration - modify as required
// The default pins are the same as HARDWARE SPI
const uint8_t chipSelect = A2;    // Also used for HARDWARE SPI setup
const uint8_t mosiPin = A5;
const uint8_t misoPin = A4;
const uint8_t clockPin = A3;

uint8_t buf[BUF_SIZE];

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile fileWrite;
SdFile fileRead;
void error(const char* str)
{
  Serial.print("error: ");
  Serial.println(str);
  if (card.errorCode()) {
    Serial.print("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1) {
    SPARK_WLAN_Loop();
  };
}

void setup() {
  Serial.begin(115200);
  while (!Serial.available()) SPARK_WLAN_Loop();
  while (Serial.available()) Serial.read();

  // initialize the SD card at SPI_FULL_SPEED for best performance.
  // try SPI_HALF_SPEED if bus errors occur.
  // Initialize HARDWARE SPI with user defined chipSelect
  if (!card.init(SPI_FULL_SPEED, chipSelect)) error("card.init failed");
  
  // Initialize SOFTWARE SPI
  //if (!card.init(mosiPin, misoPin, clockPin, chipSelect)) error("card.init failed");

  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed!");

  Serial.print("Type is FAT");
  Serial.println(volume.fatType(), DEC);

  if (!root.openRoot(&volume)) error("openRoot failed");
}

void loop() { 
  uint32_t t;
  double r;
 int remainingBytes=0;
 
  Serial.println("Type any character to start");
  while (!Serial.available()) SPARK_WLAN_Loop();
  while (Serial.available()) Serial.read();


 

  if (!fileRead.open(&root, "DSCF0003.jpg", O_RDWR)) {
    error("fileRead open failed");
  }
  
  // open or create file - truncate existing file.
  if (!fileWrite.open(&root, "DSCF0004.jpg", O_CREAT | O_TRUNC | O_RDWR)) {
    error("fileWrite open failed");
  }
  
  
  uint32_t n = fileRead.fileSize()/sizeof(buf);
  
  for (uint32_t i = 0; i < n; i++) {
    if (fileRead.read(buf, sizeof(buf)) != sizeof(buf)) {
      error("read failed");
    }

    if (fileWrite.write(buf, sizeof(buf)) != sizeof(buf)) {
      error("write failed");
    }
    Serial.print(i);
    Serial.print("/");
    Serial.println(n);
  }
  
  
   //at the end this write the remaining bytes if there are any
  remainingBytes=fileRead.fileSize() % BUF_SIZE;
  uint8_t lastbuf[remainingBytes];
  if (remainingBytes>0){
    if (fileRead.read(lastbuf,sizeof(lastbuf)) != sizeof(lastbuf)) {
        error("read failed");
    }
    if (fileWrite.write(lastbuf, sizeof(lastbuf)) != sizeof(lastbuf)) {
      error("write failed");
    }
  }
  
  
  fileWrite.close();
  fileRead.close();
  
  Serial.println("DONE!");
  
}

OK, so you have isolated it to the TCP side. Two things jump out at me:

  1. You are transfering 2.4Mbytes so size seems to matter.
  2. You malloc and free a lot–over 2400 time for this data.

For point 1, it would be good to know if the break point is a magic number like 2^20 = 1048576 bytes. There might be clues available if you could share the serial results too. You can try changing the file size in your test program to find the break point.

You should also try sending this via some other host to your server to make sure it is not a server side problem. If you are using a known FTP server, like on Linux, the odds are pretty small that it has a problem, but if you have somehow created your own server too, then you need to look at that.

For point 2: you could easily have a 1024-byte (was a 1000-byte previously) buffer that is statically allocated in global scope and avoid all the needless malloc and free calls. On a small micro like this, there is very little to be gained and a lot to be lost by calling malloc and free so often. If you are not getting red flashes when it fails, I can’t say for sure this is a problem, but it could hurt you later on.

Hi @bko,

you are right, i should be testing this with some other FTP server service, beside the one im using. Pheraps i will try to host in my machine a tcp server to see if the problem shows in this case too, even if from what i understood @nitred confirmed that no data seems to arrive after the write hangs.

The malloc and free get executed twice, i just allocate the space once and use it to send chuncks of 1024 byte, at the end i just reallocate with the remaining bytes left to send.

I tried several time now and it seems the stopping point (after which nothing is sent anymore) is always within ~1MB but there is no clear pattern here to understand why, it just happen sooner or later.
I had tried to play with the delay time so that there would be a little pause between one client.write and the other but with no luck, it does not seem to be the problem.

it seems i finally made it to work, after researching i found similar problems with TCPClient.
Long story short the spark cloud need to be disabled as is interfering somehow.

So in here explain how to do: https://community.spark.io/t/new-feature-control-your-connection/6282/5

in particular:

[QUOTE]Connect to Wi-Fi but not the Cloud: do SYSTEM_MODE(MANUAL) and call WiFi.connect() but not Spark.connect()[/QUOTE]

This is how my setup() looks for achieving this:

void setup() {
    Serial.begin(115200);
    
    // This part helps me flash new code next time, if you are not connected is not possible!
    Spark.connect();//connect to the spark cloud
    
    //we stay connected to the cloud 
    while (!Serial.available()){
          Spark.process();
          delay(500);
    }
    //**************************************************************************************
    
    
      Spark.disconnect();//so we dont have the problem tranfering files.
      //WiFi.on();
      WiFi.connect();
      
      while (!WiFi.ready()) SPARK_WLAN_Loop();
      
      LineBreaker[0]='\015';
      LineBreaker[1]='\012';
      LineBreaker[2]='\0';

  while (!Serial.available()) //Spark.process() it seems without the spark has some problem
  
  //just to consume whatever is passed in the begin to start
  while (Serial.available()) Serial.read();

    InitSD();


 connectToMyServer(FTP_ADDRESS);
}
2 Likes

The Spark cloud needs to be serviced about every 10 seconds or it will interrupt your code and try to re-establish contact. You can have the cloud and TCPClient work together by calling SPARK_WLAN_Loop() in your code anywhere you will block for more than 10 seconds. So putting that in your for loop will allow you to have both.

thanks, now i understand, ill do that then :slight_smile:

Hi @Dave @bko

I know I haven’t been helpful in resolving this issue, but the issue of hanging persisted for the last two months. The hang used to happen within 12 hours of a tcp connection being open continuously with a NodeJS server.

But, on 27th Nov, I updated 4 Sparks to CC3200 v1.13 (I think) using the cc3000-patch-programmer : https://github.com/spark/cc3000-patch-programmer.

Then I kept the 4 Sparks alive for the past 4 days and they haven’t experienced any Hang so far. So I assume that it was the problem. The v1.13 release notes mention the same error as well : http://processors.wiki.ti.com/index.php/CC3000_Release_Notes

I think I can mark the topic as Solved :smile:
Thanks guys

2 Likes