Sending SMS message vis GPRS SIM900A with Arduino UNO is not reliable

I am using the code furnished below to send an sms to a SIM900A GPRS module ( without shield ) via a Altsoft software serial. I am using Arduino UNO in this project. Most of the time, it works but sometimes it doesn't. I understand I am supposed to receive an "OK" response to confirm that the sms has been send correctly. I the "OK" response failed to display many times inspite of the sms message being sent correctly. So , I remove them temporarily. How can I improve this code ? How can I send an sms message more reliably?

AltSoftSerial altSerial;
char sms[120];

void SendSMS() {
	for (int repeats = 0; repeats < 1; repeats++) {
		altSerial.println("AT+CMGS=\"+436033000000\"");  
		delay(3000);
		if (printSerial(">")) { Serial.println("Ready for SMS"); }
		else { Serial.println("Not ready for SMS"); continue; }

		for (int i = 0; i < strlen(sms); i++) {
			altSerial.print(sms[i]);
			delay(10);
		}

		altSerial.write(0x1A); //println((char)26);

		bool time_out = false;
		unsigned long start_time = millis();
		while (!printSerial("OK"))
		{
			delay(1000UL);
			if (millis() - start_time > 20000UL) {
				time_out = true;
				break;
			}
		}
		if (!time_out) { Serial.println("SMS Sent");  break; }
		else { Serial.println("SMS not sent");  continue; }
	} 
}

we miss information on what printSerial() is

may be the timeout is too short? may be using delay() for 1s is a bad idea and you flood the incoming buffer and loose the OK (if there is a lot of chatter happening during that second)

Thanks for the observation. Please suggest a solution.

May be posting the code for printSerial() as requested?
you could also increase the timeout (change 20000UL but that's already 20s so...)
of course there is the possibility of Software Serial / altSerial to miss a beat depending on baud rate used... which we don't know about...

Don't post snippets (Snippets R Us!)

Here's the code for printSerial():


bool printSerial(char* response)
{
  char buffer[120];
  int i = 0;
  while(altSerial.available()>0 && i<118){
    char c = altSerial.read();
    buffer[i++] = c;
    Serial.print(c); //DEBUG
  }
   buffer[i++] = '\0';
   
   if(response != NULL){
      char* p = NULL;
      p = strstr(buffer, response);        
      if( p!= NULL) return true;
   }
  return false;
}

it's an error prone way to scan the incoming buffer for a specific string... As you empty the incoming buffer very fast, you could get the O in the first scan and the K in the second scan and your code will never see the "OK"

The "OK" response normally comes together like so, but misses sometimes.

SMS sent

it does not come "together". It comes one byte after the other, at the Serial connexion baud rate. Your loop empties the incoming buffer likely faster than the bytes arrive

Murphy laws says that from time to time the receiving will stop between the O and the K and thus you will not find "OK" in the received string... that's a possibility to consider

Any fix?

rewrite the way you listen to the response. Don't use delay. just listen and keep a rolling buffer of 2 bytes and compare with "OK" (until a given timeout)

Thank you much for your time. I will check with the new settings.

that's an example I wrote some time back. May be it can give you some ideas

enum t_status : uint8_t {ONHOLD, WAITING, OK_FOUND, TIMEOUT};
t_status waitForOK(bool getReady = false, uint32_t waitingTime = 2000ul) {
  static char buffer[2];
  static uint32_t startTime;
  static uint32_t timeout;
  static t_status currentState = ONHOLD;

  if (getReady) { // prepare to receive
    startTime = millis();
    buffer[0] = buffer[1] = '\0';
    timeout = waitingTime;
    currentState = WAITING;
    return WAITING;
  } else {
    if ((currentState != ONHOLD) && (millis() - startTime >= timeout)) {
      currentState = ONHOLD;
      return TIMEOUT;
    }
    if (Serial.peek() == -1) return currentState;

    if (currentState == WAITING) {
      buffer[0] = buffer[1];
      buffer[1] = Serial.read();
      if (strncmp(buffer, "OK", 2) == 0) {
        currentState = ONHOLD;
        return OK_FOUND;
      }
    }
  }
  return currentState;
}

void setup() {
  Serial.begin(115200);
  Serial.println("\nYou have 5s to type OK");
  waitForOK(true, 5000);
}

void loop() {
  switch (waitForOK()) {
    case ONHOLD: break;
    case WAITING: break;
    case OK_FOUND:
      Serial.println("I got OK.");
      if (Serial.available()) {
        Serial.println("There was some stuff in the incoming buffer after the OK");
        while (Serial.available()) {
          char r = Serial.read();
          Serial.print("0x");
          Serial.print(r, HEX);
          if (isalnum(r)) {
            Serial.print("\t('");
            Serial.write(r);
            Serial.println("')");
          } else if (r == '\n') Serial.println("\t(NL)");
          else if (r == '\r') Serial.println("\t(CR)");
          else Serial.println();
        }
      }
      Serial.println("Done");
      break;
    case TIMEOUT:
      Serial.println("I did not get OK, timeout");
      Serial.println("You have 5s to type OK, try again");
      waitForOK(true, 5000);
      break;
  }
}

run it with Serial monitor at 115200 bauds

it will ask you to type "OK" in the Serial console. You can type other stuff, it will only stop when it gets to OK. when it gets "OK" it will report also what's left in the Serial buffer (by reading it)

Sounds interesting. Many thanks. I will have a look. But need some time to do so.