Trying to make a GSM code without using delay

Hello,

So i am currently working on a project that requires constantly sending data to thingspeak server using GSM. I recently started learning how GSM (SIM808) works and i have followed basic tutorials and codes to send data to thingspeak server using GSM but i have noticed that i has a too many delays and with my past experience i can say that using delays messes with rest of the program execution. And in my code there is a lot of PID motor control and other calculations so i am trying to make a code without delays. I know about millis() but i don't exactly know how to use it efficiently. So here's the code.

#define GSM Serial2

int seq, seq_comp = 0;
String ack, dummy ;
int x,y,z = 0;

void ShowSerialData()
{
  // while (GSM.available() != 0)
  // { 
  //   Serial.write(GSM.read());
  // }

  // while (Serial.available() != 0)
  // {
  //   GSM.print(Serial.read());
  // }
  // delay(10);
  Serial.println(seq);
  Serial.print(" ");
//  Serial.println(ack);

}

void setup() 
{
  GSM.begin(9600);
  Serial.begin(115200);
}

void loop() 
{
    if (seq == 0)
    {
      GSM.println("AT");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 1)
    {
      GSM.println("AT+CPIN?");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "ERROR")
      {
        seq = 1;
        ack = dummy;
      }
      else
      {
        seq++;
        ack = dummy;
      }
      
    }

    else if (seq == 2)
    {
      GSM.println("AT+CREG?");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "ERROR")
      {
        seq = 2;
        ack = dummy;
      }
      else
      {
        seq++;
        ack = dummy;
      }
      
    }

    else if (seq == 3)
    {
      GSM.println("AT+CGATT?");
      ack = GSM.read();
      if (ack == "ERROR")
      {
        seq = 3;
        ack = dummy;
      }
      else
      {
        seq++;
        ack = dummy;
      }
      
    }

    else if (seq == 4)
    {
      GSM.println("AT+CIPSHUT");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "SHUT OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 5)
    {
      GSM.println("AT+CIPSTATUS");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 6)
    {
      GSM.println("AT+CIPMUX=0");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 7)
    {
      GSM.println("AT+CSTT=\"airtelgprs.com\"");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 8)
    {
      GSM.println("AT+CIICR");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK") // Wireless connection
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 9)
    {
      GSM.println("AT+CIFSR"); // change ACK
      ack = GSM.read();
      ShowSerialData();
      if (ack == "ERROR")
      {
        seq = 9;
        ack = dummy;
      }
      else
      {
        seq++;
        ack = dummy;
      }
      
    }

    else if (seq == 10)
    {
      GSM.println("AT+CIPSPRT=0");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "OK")
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 11)
    {
      GSM.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\""); // change ack
      ack = GSM.read();
      ShowSerialData();
      if (ack == "ERROR")
      {
        seq = 11;
        ack = dummy;
      }
      else
      {
        seq++;
        ack = dummy;
      }
      
    }

    else if (seq == 12)
    {
      GSM.println("AT+CIPSEND");
      String str = "GET https://api.thingspeak.com/update?api_key=Z5W13HEYV8ZRTZNI&field1=" + String(x) + "&field2=" + String(y) + "&field3=" + String(z);
      GSM.println(str);
      ack = GSM.read();
      ShowSerialData();
      if (ack == "SEND OK")
      {
        seq++;
        ack = dummy;
      }
    }


    else if (seq == 13)
    {
      GSM.println((char)26);
      ack = GSM.read();
      ShowSerialData();
      if (ack > 0)
      {
        seq++;
        ack = dummy;
      }
    }

    else if (seq == 14)
    {
      GSM.println("AT+CIPSHUT");
      ack = GSM.read();
      ShowSerialData();
      if (ack == "SHUT OK")
      {
        seq++;
        ack = dummy;
      }
    }
}

The code is pretty simple if the "AT" command returns "OK" then it would move to the next AT command, if returns an error then that AT command is repeated. Some of the commands don't return "OK", they return some other values or modes selected, so in that case i set the condition if the acknowledgment is "ERROR" then the sequence number remains the same, else go to the next sequence. The way i verify the acknowledgment is that after sending the AT command i read the acknowledgment by saving it in a string variable "ack" and then compare it with the string, if identical then move to the next command and clear the "ack" variable. I have not worked with "Strings" before so i don't exactly know how they work in conditional statements. Now the problem in this code is that it won't move past the first sequence so something is wrong in the conditional statement but i don't know what?

Serial.read returns a single char if there is one, or -1 if there isn't. You need to use Serial.available to see if anything is there and accumulate what you read into your String. Or you could try readStringUntil.

readStringUntil() is blocking too, no better than delay()!

Typically event driven code using serial looks like:

  if (Serial.available() > 0)   // non blocking check
  {
    char ch = Serial.read() ;  // we know theres at least one character to read
    if (ch == ENDCHARACTER)  // probably a newline or similar after the "OK"...
    {
      buffer [ptr] = 0 ; // null terminator
      handle_packet (buffer) ;   // deal with the packet now its complete
      ptr = 0 ;    // reset for next packet
    }
    else 
    {
      if (ptr < BUFFER_SIZE-1)  // important to prevent buffer overruns
        buffer[ptr++] = ch ;
      else
        ..... deal with buffer overflow gracefully somehow...
    }
  }

From the point of view of the rest of the code all that happens is every so often handle_packet()
gets called. If you expect an "OK", you need to set up global state that handle packet can use to
know what it should do with the returned string (ie check if its "OK" or not, and set some flags?)

Then rather than busy waiting for the reply you code will be checking the flag to see if its time
to perform the next step. The state-machine formalism is normally used for programming this
event-driven style.

The advantage of never busy-waiting or calling delay() is that other parts of the sketch can progress
normally all the time, unimpeded by the serial handling.

MarkT:
readStringUntil() is blocking too, no better than delay()!

Typically event driven code using serial looks like:

  if (Serial.available() > 0)   // non blocking check

{
    char ch = Serial.read() ;  // we know theres at least one character to read
    if (ch == ENDCHARACTER)  // probably a newline or similar after the "OK"...
    {
      buffer [ptr] = 0 ; // null terminator
      handle_packet (buffer) ;  // deal with the packet now its complete
      ptr = 0 ;    // reset for next packet
    }
    else
    {
      if (ptr < BUFFER_SIZE-1)  // important to prevent buffer overruns
        buffer[ptr++] = ch ;
      else
        ..... deal with buffer overflow gracefully somehow...
    }
  }



From the point of view of the rest of the code all that happens is every so often handle_packet()
gets called. If you expect an "OK", you need to set up global state that handle packet can use to
know what it should do with the returned string (ie check if its "OK" or not, and set some flags?)

Then rather than busy waiting for the reply you code will be checking the flag to see if its time
to perform the next step. The state-machine formalism is normally used for programming this
event-driven style.

The advantage of never busy-waiting or calling delay() is that other parts of the sketch can progress
normally all the time, unimpeded by the serial handling.

That's actually smart. Rather than comparing strings i should just check if i received the desired acknowledgment and just set a flag. Thank you sir i will surely try.