Software Serial Buffer overflow with 3G module

I have an ITEAD 3G module running off my arduino. It had been running fine for months but then stopped responding from commands given by my web server. I suspect some change was made to the HTTP Header protocol because now the commands are getting cut off. I believe this is to do with the software serial buffer size but I have already increased this to 256 in the softwareserial.h file.

How can I improve my code to prevent the buffer overflow?

// Included header files
#include <SoftwareSerial.h>


// AT command related
#define AT_CREG_QUERY ("AT+CREG?")
#define AT_CHTTPACT_COMMAND ("AT+CHTTPACT=\"%s\",%d")
#define AT_CHTTPACT_INIT_RESPONSE ("+CHTTPACT: REQUEST")
#define AT_CHTTPACT_DATA_RESPONSE ("+CHTTPACT: DATA,")
#define AT_CHTTPACT_DONE_RESPONSE ("+CHTTPACT:0")

#define EOS ('\0')
#define ESC (0x1A)
#define OK ("OK")

// GSM definitions
#define unoTXPin (5) // Uno -> GSM
#define unoRXPin (4) // Uno <- GSM
#define powerGSMPin (7)
#define GSMBaudRate (9600)
#define UnoBaudRate (9600)

SoftwareSerial gsmSerial(unoRXPin,unoTXPin);


#define TEST_REQUEST ("GET //itest.php HTTP/1.1\r\nHost:website.com:80\r\nContent-Length:0\r\n\r\n")

char aux_str [100];

int8_t answer;
int aux = 0;
int data_size = 0;


#define HOST_NAME ("website.com") 
#define HOST_PORT (80)

void setup() {

    
  // init sequence
  Serial.begin(UnoBaudRate);
  gsmSerial.begin(UnoBaudRate);
  Serial.println("Turning GSM on");
  gsm_power_on();
  while( (sendATcommand(AT_CREG_QUERY, "+CREG: 0,1", 500) || 
    sendATcommand(AT_CREG_QUERY, "+CREG: 0,5", 500)) == 0 );
  Serial.println(F("GSM : Registered to network"));
  Serial.println(F("GSM : Ready for AT command"));
}
void loop () {

    sprintf(aux_str, AT_CHTTPACT_COMMAND, HOST_NAME, HOST_PORT);
    Serial.println(F("\n*** Start HTTP Transaction ***\n"));
    Serial.println(AT_CHTTPACT_INIT_RESPONSE);

    answer = sendATcommand(aux_str, AT_CHTTPACT_INIT_RESPONSE, 60000);
    Serial.println(aux_str);
    if (answer == 1)
    {  

      data_size = 0;
      
      Serial.println(F("Sends Request:"));
      Serial.println(TEST_REQUEST);    
      gsmSerial.println(TEST_REQUEST);
      // Sends 
      aux_str[0] = ESC;
      aux_str[1] = 0x00;
      answer = sendATcommand(aux_str, AT_CHTTPACT_DATA_RESPONSE, 100000);
      delay(2000);
      if(answer == 1)
      {
        Serial.println(AT_CHTTPACT_DATA_RESPONSE);
        
          while(gsmSerial.available() > 0) {
            Serial.print((char)gsmSerial.read());
            delay(5);
          }
        
        }

      Serial.println(F("\n*** End HTTP Transaction ***\n"));
      Serial.print(F("Memory Free : "));
      Serial.println(memoryFree());
      delay(30000); // halt
    }



  
}
// variables created by the build process when compiling the sketch
extern int __bss_end;
extern void *__brkval;
// function to return the amount of free RAM
int memoryFree(){
  int freeValue;
  if((int)__brkval == 0) 
    freeValue = ((int)&freeValue) - ((int)&__bss_end);
  else 
    freeValue = ((int)&freeValue) - ((int)__brkval);
  return freeValue;
}

void gsm_power_on(){

  uint8_t answer=0;

  // checks if the module is started
  answer = sendATcommand("AT", "OK", 2000);
  if (answer == 0)
  {
    // power on pulse
    digitalWrite(powerGSMPin,HIGH);
    delay(200);
    digitalWrite(powerGSMPin,LOW);
    delay(6000);
    // waits for an answer from the module
    while(answer == 0){    
      // Send AT every two seconds and wait for the answer
      answer = sendATcommand("AT", OK, 2000);    
    }
  }

}

int8_t sendATcommand(char* ATcommand, char* expected_answer1,
unsigned int timeout)
{

  uint8_t x=0,  answer=0;
  char response[100];
  unsigned long previous;

  memset(response, EOS, 100);    // Initialize the string

  delay(100);

  while( gsmSerial.available() > 0) gsmSerial.read();    // Clean the input buffer

  gsmSerial.println(ATcommand);    // Send the AT command 


    x = 0;
  previous = millis();

  // this loop waits for the answer
  do{

    if(gsmSerial.available() != 0){    
      response[x] = gsmSerial.read();
      x++;
      // check if the desired answer is in the response of the module
      if (strstr(response, expected_answer1) != NULL)    
      {
        answer = 1;
      }
    }
    // Waits for the asnwer with time out
  }
  while((answer == 0) && ((millis() - previous) < timeout));    

  return answer;
}

int8_t sendATcommand2(char* ATcommand, char* expected_answer1,
char* expected_answer2, unsigned int timeout)
{

  uint8_t x=0,  answer=0;
  char response[100];
  unsigned long previous;

  memset(response, EOS, 100);    // Initialize the string

  delay(100);

  while( gsmSerial.available() > 0) gsmSerial.read();    // Clean the input buffer

  Serial.println(ATcommand);    // Send the AT command 


    x = 0;
  previous = millis();

  // this loop waits for the answer
  do{

    if(gsmSerial.available() != 0){    
      response[x] = gsmSerial.read();
      x++;
      // check if the desired answer is in the response of the module
      if (strstr(response, expected_answer1) != NULL)    
      {
        answer = 1;
      }
      // check if the desired answer is in the response of the module
      if (strstr(response, expected_answer2) != NULL)    
      {
        answer = 2;
      }
    }
    // Waits for the asnwer with time out
  }
  while((answer == 0) && ((millis() - previous) < timeout));    

  return answer;
}

Example of response

+CHTTPACT: DATA,
660
http/1.1 200 ok
server: nginx/1.12.0
date: wed, 14 jun 2017 01:31:41 gmt
content-type: text/html; charset=utf-8
content-length: 416
connection: keep-alive
vary: accept-encoding
x-acc-exp: 600
x-proxy-cache: hit website.com

<!DOCT
*** End HTTP Transaction ***

The cut off happens when it starts to read the html of the website.

I also get the following response where it does not even begin to read the html. I believe this is because the transfer-encoding is 'chunked' but I'm not sure how to solve this. Or it could still just be the bugger size.

+CHTTPACT: DATA,
571
http/1.1 200 ok
server: nginx/1.12.0
date: wed, 14 jun 2017 01:34:46 gmt
content-type: text/html; charset=utf-8
transfer-encoding: chunked
connection: keep-alive
vary: accept-encoding
x-acc-exp: 600
x-proxy-cache: hit website.com

*** End HTTP Transaction ***

and another buffter overflow

+CHTTPACT: DATA,
575
http/1.1 200 ok
server: nginx/1.12.0
date: wed, 14 jun 2017 01:34:09 gmt
content-type: text/html; charset=utf-8
transfer-encoding: chunked
connection: keep-alive
vary: accept-encoding
x-acc-exp: 600
x-proxy-cache: expired website.co
*** End HTTP Transaction ***

How can I improve my code to prevent the buffer overflow?

Remove the delay in this loop:

         while(gsmSerial.available() > 0) {
            Serial.print((char)gsmSerial.read());
            delay(5);
          }

This allows 4 extra characters to arrive after reading 1. Eventually, the input buffer overflows.

That will probably make it work, but I believe the correct thing to do is watch for the "content-length" line, and expect the number of bytes it specifies. You could pair that with a timeout, in case you miss some of the content.

Here's a post that has a Finite-state Machine watch for that line. It saves the content, but you could just return and then handle the content as it arrives (i.e., forward it).

You should also consider using AltSoftSerial or NeoSWSerial. Both are much more efficient than SoftwareSerial, which disables interrupts for long periods of time. And it cannot transmit and receive at the same time.

AltSoftSerial requires two specific pins (8 & 9 on an UNO). NeoSWSerial works on any two pins, but only at baud rates 9600, 19200 and 38400. They are both drop-in replacements for SoftwareSerial. NeoSWSerial is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

hi dev,

I have removed the delay(5), but that has not helped.

I installed the NeoSWserial library and substituted but it cuts out even earlier. I just need the information at the end of the serial data. Is there an easy way to wipe the first 400 characters and just read the end. I think part of the problem is my website retrieves data from a server so if I start reading from the serial too early it doesn't get a response at all

+CHTTPACT: DATA,
660
http/1.1 200 ok
server: nginx/1.12.0
date: wed, 14 jun 
*** End HTTP Transaction ***

OK Update while writting this. The problem appears to be two things. 1) If i didn't have the delay before reading from serial I would not get an response from the website or only part of the response so I put a 2 sec delay to allow the server to run queries and respond.
2) Doing this means the serial has overflown while waiting for the server.

To solve this I put a while loop timer to stream from the website for 5 seconds and I retrieve the full page. What a relief! Thanks Dev, you got me started in the right direction

      if(answer == 1)
      {
        while(milis()-lastmillis<5000){
        
          while(gsmSerial.available() > 0) {
            Serial.print((char)gsmSerial.read());
           
          }
        }
      }

acitta:
To solve this I put a while loop timer to stream from the website for 5 seconds and I retrieve the full page.

Waiting a specific time for a message is never going to be reliable - especially with data coming from the internet.

Your program needs to be looking for some character (or characters) that identify the end of the message.

Have a look at how the 2nd and 3rd examples in Serial Input Basics work and see if you can apply the concept to your project.

...R