Get rid of the delays and make a "wait for response" function

Hello i have this code to send an email using GSM from arduino to another email.
it is working correctly but i have to put some delays in order to wait for a response from the server.
my target is to get rid of the delays and make a function that waits for a response with a parameter of the response i want it to wait (since some commands have different responses like "OK" or "ACCEPTED").
I'm a noob at this stuff and i am in need for some guidance.

#include <AltSoftSerial.h> //Include all relevant libraries, emulate an additional serial port, allowing you to communicate with another serial device
AltSoftSerial GSM; // The serial connection object to the GPS device
void setup() {
  Serial.begin(115200); // Begin the serial connection
  GSM.begin(115200); //Default baud-rate for the board
  delay(200);
  GSM.println(F("AT+IPR=9600")); // Change baud-rate for better communication
  delay(600);
  Serial.begin(9600); // Begin the new serial connection
  GSM.begin(9600); // Begin the new serial connection for the board
  delay(200);
  Serial.println(F("Welcome to my Arduino project")); // The (F("")) acts like a macro to store these strings to program storage space and free-up dynamic memory
  GSM.println(F("AT")); // Communication test command
  delay(500);
  Serial.println(F("Initialising"));
  GSM.println(F("AT+GPS=1")); // Enable GPS
  delay(500);
  Serial.println(F("."));
  delay(500);
  Serial.println(F("."));
  GSM.println(F("AT+CMGF=1")); // Enable Text-mode
  delay(500);
  Serial.println(F("."));
  delay(500);
  Serial.println(F("Operation started SUCCESSFULLY"));
  GSM.println(F("AT+GPSRD=0")); // Disable the GPS Raw Data flow (just in case)
  delay(500);
  Serial.println(F("Sending email"));
  delay(500);
  GSM.println(F("AT+CGATT=1"));
  delay(4000);
  GSM.println(F("AT+CGDCONT=1,\"IP\",\"internet\""));
  delay(1000);
  GSM.println(F("AT+CGACT=1,1"));
  delay(4000);
  while (GSM.available())  // forward data from esp to monitor
    Serial.write(GSM.read());
  GSM.println(F("AT+CIFSR"));
  delay(1000);
  GSM.println(F("AT+CIPSTART=\"TCP\",\"smtp-relay.sendinblue.com\",587"));
  delay(4000);
  CIPSEND(30,"EHLO smtp-relay.sendinblue.com");
  CIPSEND(10,"AUTH LOGIN");
  CIPSEND(24,"XXXXXXXXXXXXXXXXXXXXXXXX");
  CIPSEND(24,"XXXXXXXXXXXXXXXXXXXXXXXX");
  CIPSEND(31,"MAIL FROM: <XXXXXXXXXXXXXXX>");
  CIPSEND(33,"RCPT To: <XXXXXXXXXXXXXXXXX>");
  CIPSEND(4,"DATA");
  CIPSEND(26,"To: XXXXXXXXXXXXXXXXXX");
  CIPSEND(24,"From: XXXXXXXXXXXXXXXXX");
  CIPSEND(25,"Subject: Your Arduino\r\n");
  CIPSEND(8,"Location");
  CIPSEND(1,".");
  delay(2000);
  CIPSEND(4,"QUIT");
  Serial.println(F("Done"));
}
void CIPSEND(int x,String Command)
{
  GSM.print(F("AT+CIPSEND="));
  GSM.println(x);
  delay(200);
  GSM.println(Command);
  delay(1200);
  while (GSM.available())  // forward data from GSM to monitor
    Serial.write(GSM.read());
}
void loop() {}

themtsil:
my target is to get rid of the delays and make a function that waits for a response

Your use of the phrase "waits for response" seems to me to imply the equivalent of a delay().

Perhaps you want the program to send a message and then, while checking regularly for a response, go about other tasks. if so, have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

You may also want read up about the concept of a "state machine". Basically it is the use of one or more variables that keep track of progress through a sequence of actions. For example

  • XXX sent,
  • waiting for JJJ
  • YYY sent
  • waiting for MMM

As each action completes it updates the value in the state variable and other actions operate (or not) depending on the value of the variable.

...R

Your use of the phrase "waits for response" seems to me to imply the equivalent of a delay().

My problem with delay() is that in order for my code to work and have the minimun delay in the exact time i have to run the program again and again decreasing the delay in every command until i find a certain threashold in which the delays are low enough but dont mess my code ( meaning that i get the response).
So i thought that i would be way more efficienct if i make the program wait until i get a certain response so i dont have to mess with delays every single time. i dont want it to do other tasks while it waits.

Basically i just want to get the best delay "amount" automatically.

First, why do you do 2 Serial.begin with 2 different baudrates?

  Serial.begin(115200); // Begin the serial connection
 ...  
  Serial.begin(9600); // Begin the new serial connection

Only one is required, you have to choose according to the value set in your serial monitor.

Basically i just want to get the best delay "amount" automatically.

Are you talking about the individual values of all these instructions ?

delay(200);
delay(600);
...

The only way I can think of would be to check the value returned by the instruction just before each delay and wait until it reaches the correct value. Easier said than done...

For example:

  GSM.println(F("AT+CMGF=1")); // Enable Text-mode
  delay(500);

GSM.println returns the number of bytes written, though reading that number is optional. Data type: size_t.

This said, I don't really know how to check this number and wait until it reaches the expected value to pass to the next instruction.

Another way would be to write your own "println" function, which would send each character separately, and wait each time for the correct amount of milliseconds (assuming it remains constant) and sends the EOL & CR codes at the end. This individual delay, you could find it by trials and errors (just like you do now, but only for 1 character)

Only one is required, you have to choose according to the value set in your serial monitor.

true xD
i need to change the baud rate from my gsm every time i reset it so thats why i'm doing the gsm baudrate change. but the serial is no need you are right.

Are you talking about the individual values of all these instructions ?

yes

i made a code that does what i want to do . i only tested it a few times but for now it works.

char textMessage[100] = {};
char* pch;
boolean wait;
void GetResponse(char response[7])
{
  wait=true;
  while (wait==true)
  {
    if (GSM.available() > 0) //If there is stuff in the buffer
    {
      byte numChars = GSM.readBytes(textMessage, 100);//.readBytes returns number read
      textMessage[numChars] = '\0'; //null Terminator
      Serial.println(textMessage); // Print the new message
      pch = (strstr(textMessage, response)); //get the keyword from message
      if (pch) wait = false;
    }
  }
}

if you think i should change something please do say so.

To do this properly is a bit of work, and you also need to accommodate that condition when the modem returns ‘something else’ or doesn’t reply at all (timeout).

You need to explore asynchronous coding styles, which allow other tasks to occur while the serial runs in the background.

Yeah , now if a command give an error it will stack in a loop. So I am thinking to add a timeout and if the timeout happens then maybe reset the gsm module and run this code from the beginning to resend the email

The method you have constructed may (probably will) get you incomplete responses. At Baud-rate 9600, every byte being sent takes over 1ms. You may even get your keyword spread out over to responses. You should look at Robin's Serial Input Basics examples (for true non-blocking) or you could consider reading bytes one by one until you are satisfied that the buffer is empty (and no longer filling up )

Thank you for your response I will try to find a better way from the link you show me and if I can't I will probably stick with this or the delays.