Interfacing GSM module code optimization

I’ve interfaced a GSM module with an Arduino to communicate with my phone. At this point I can switch an LED by texting “On/Off” and receive a text with temperature/humidity values from a DHT11 sensor by texting “Temp” (the code below is a simplified testing version that only sends back a constant integer).

The code is working but I would like to ask for your suggestions to make it more robust and less verbose before I move forward. I would also like to get rid of delay() by using a function that reads the OK sent by the modem (after an AT command is successfully handled), before the next AT command is issued.

The following site lets readers edit the code:

https://codeshare.io/aypk6e

Hastebin creates a duplicate after editing:

https://hastebin.com/axuxatosoh.cpp

Traditional pasting:

/*-----------------------------------------------------------------------------------------
Modified version of http://arduino.ru/forum/apparatnye-voprosy/datchiki-temperatury-ds18b20
*******************************************************************************************
Light LED with SMS "On" and vice-versa, "Temp" to receive SMS with the number of the sender
(TODO replace with variable holding sensor data)
-------------------------------------------------------------------------------------------
M590E GSM Module working voltage always above 3.5V and under 4.2V
Use 3.3V level-shifter for Tx/Rx when using 5V Arduino
Short Gnd and Boot pins to auto-boot on power ON
-----------------------------------------------------------------------------------------*/
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 4); // RX, TX

int greenLED = 7; // ON when startup procedure is over // modem is ready to handle text messages
int yellowLED = 8; // SMS triggered with "On" or "Off"
int redLED = 9; // extra LED for testing

boolean startup_sequence = true; // flag startup sequence must take place

// hold what's available through the serial port
int ch = 0;
String val = "";

String master = "+************"; //replace with sender phone number

const unsigned long baudrate = 38400;  // baudrate for both Hardware Serial and Software Serial

//SMS sending function
void sms(String text, String phone) {
	mySerial.print("AT+CMGD=1,4\n\r"); // Delete all messages so there are no conflicts with accidentally unread or stored SMS
	delay(1000);
	mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
	delay(1000);
	mySerial.print("\"" + text + "\""); // containing desired text
	delay(1000);
	mySerial.print((char)26); // signals end of text message
	delay(2000);
}

void setup()
{
	mySerial.begin(baudrate);  // Set the baudrate of the GSM/GPRS Module.
	Serial.begin(baudrate);    // Set the baudrate of the serial monitor, start serial port at 38400 bps

	pinMode(greenLED, OUTPUT);
	pinMode(yellowLED, OUTPUT);
	pinMode(redLED, OUTPUT);
}

void loop()
{
	// do nothing else until we receive +PBREADY from the modem, which signals it's ready to receive AT commands
	while (startup_sequence == true)
	{
		if (mySerial.available()) // listen to the module 
		{
			delay(200);
			ReadChar(); // read and hold incoming data
			if (val.indexOf("+PBREADY") > -1) // mode is ready receive AT commands to enable handling text messages
			{
				Serial.println(val); // for debugging
				InitModem(); // contains flag indicating startup_sequence has taken place
			} else {
				Serial.println(val);
			}
		}
		val = ""; // clear val and move on
	}

	// startup sequence has taken place, let's listen for SMS
	if (mySerial.available())
	{
		delay(200);
		ReadChar();
		Serial.println(val);
		if (val.indexOf("+CMT") > -1) // an SMS has been received
		{
			if (val.indexOf(master) > -1) // by a known phone number
			{
				handleSMS(); // do something according to text message content
			}
		}
		else {
			Serial.println(val); // not an SMS, print it to Serial Monitor
		}
		val = ""; // clear what val was holding and let's listen for more
	}
}
void InitModem() 
{
	mySerial.print("AT+CMGD=1,4\n\r");        // Delete all messages so there are no conflicts with accidentally unread or stored SMS
	delay(1000);
	mySerial.print("AT+CMGF=1\n\r");          // Set the module message mode, set either at 0 or 1 : 0 = PDU / 1 = Text mode.
	delay(1000);
	mySerial.print("AT+CSCS=\"GSM\"\r");      // Set the character sets : “GSM” = default alphabet (GSM03.38.6.2.1)
	delay(1000);
	mySerial.print("AT+CNMI=2,2,0,0,0\n\r");  // Set message indication Format, AT+CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]]
	// Where <mode> = 2: under On-line State, message indication code is cashed in module. When
	// processing released, output indication code through serial port. Under its state,
	// display indication code on terminal equipment directly.
	// Where <mt> = 1: the message content is not displayed directly but storaged.
	// Where <mt> = 2: the message content is displayed directly but not storaged.
	delay(1000);
	digitalWrite(greenLED, HIGH); // signal InitModem finished, modem ready to receive commands
	sms(String("Module Active"), String(master));
	delay(2000);
	startup_sequence = false;
}

void ReadChar() 
{
	while (mySerial.available())
	{
		ch = mySerial.read();
		val += char(ch);
		delay(50);
	}	
}

void handleSMS()
{
	if (val.indexOf("On") > -1)
	{
		digitalWrite(redLED, HIGH);
	}
	if (val.indexOf("Off") > -1)
	{
		digitalWrite(redLED, LOW);
	}
	if (val.indexOf("Temp") > -1)
	{
		sms(String("T1=" + String(master) + ",  T2=" + String(master)), String(master));
	}	
}

/*-----------------------------------------------------------------------------------------
// ****************************************************************************************
// TODO replace delay() with function that waits for OK after an AT command has been issued
// before issuing the next one.

// http://arduino.ru/forum/apparatnye-voprosy/ne-rabotaet-neoway-m590?page=3#comment-183631

// ****************************************************************************************

String line; // это где-то глобально объяви

void GotLineFromNeoway(const String& line)
{
// Received a response line from Neoway. Any answers come in the form of
// either one or several lines, so that the unit we need
// operate - it is the string
//  if(line == F("ERROR")) // error
// if(line == F("OK")) // all OK
// if(line.startsWith(F("+CMT"))) // SMS has arrived  
}

char ch;
while(Serial.available())
{
ch = Serial.read();
if(ch == '\r') continue;
if(ch == '\n') { GotLineFromNeoway(line); line = ""; }
else
	line += ch;
}  
----------------------------------------------------------------------------------------*/

Thank you in advance,

Francisco

The easy optimisation is to change int greenLED = 7 etc. to byte greenLED = 7 . You save one byte each time.

Harder is to get rid of the delays. You have to set a series of state flags and check / set (if necessary) the state every loop iteration. Here is an example based on your code. Of course it was much simpler with delays, but these block any other code until they expire.

void sms() {
  // called every loop iteration
  // parameters smsSend, state, text and phone set as globals
  // Initial state is DELETE_ALL_MESSAGES

  if ( smsSend == true ) {  // if true, we are handling a message send

    if ( state == DELETE_ALL_MESSAGES ) {
      // set timeout_timer here (if required)
      mySerial.print("AT+CMGD=1,4\n\r");
      state = WAITING_RESPONSE_DELETE_ALL_MESSAGES ;
    }
    else if ( state == WAITING_RESPONSE_DELETE_ALL_MESSAGES ) {
      if (  test_mySerial_Return() == true )  {  // we've got a valid return code from the command
        // we've got a valid return code so set up target phone
        mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
        state = WAITING_RESPONSE_SETUP_TARGET_PHONE ;
      }
    }
    else if ( state == WAITING_RESPONSE_SETUP_TARGET_PHONE ) {
      if (  test_mySerial_Return() == true )  {
        // we've got a valid return code so send text
        mySerial.print("\"" + text + "\""); // containing desired text
        state = WAITING_RESPONSE_SEND_TEXT_TO_TARGET_PHONE ;
      }
    }
    else if ( state == WAITING_RESPONSE_SEND_TEXT_TO_TARGET_PHONE ) {
      if (  test_mySerial_Return() == true )  {
        // we've got a valid return code so send end of message
        mySerial.print((char)26); // signals end of text message
        state = WAITING_RESPONSE_SEND_END_OF_MESSAGE ;
      }
    }
    else if ( state == WAITING_RESPONSE_SEND_END_OF_MESSAGE )  {
      if (  test_mySerial_Return() == true )  {
        // we've got a valid return code so we've successfully sent message
        state = MESSAGE_SENT_OK ;
        smsSend = false ;
      }
    }
    // test timeout timer here and fail if we're still here after 5 seconds (if required).
  }
}

Getting rid of delays in code that’s not weaved and can be called over and over.

//SMS sending function un-delayed

void sms(String text, String phone) {

  static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
  static byte procState;

  if ( waitTimer > 0 )
  {
    if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
    else                                                                  waitTimer = 0; // and on to the switch-case      
  }

  switch ( procState )
  {
        case 0 :
	mySerial.print( F( "AT+CMGD=1,4\n\r" )); // Delete all messages so there are no conflicts with accidentally unread or stored SMS
        procState = 1;      //  run case 1 next time
        waitTimer = 1000;
        startTimer = millis();
        break;

        case 1 :
	mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
        procState = 2;      //  run case 2 next time
        waitTimer = 1000;
        startTimer = millis();
        break;

	case 2 :
	mySerial.print("\"" + text + "\""); // containing desired text
        procState = 3;      //  run case 1 next time
        waitTimer = 1000;
        startTimer = millis();
        break;

	case 3 :
	mySerial.print((char)26); // signals end of text message
        procState = 0;      //  run case 0 next time
        waitTimer = 2000;
        startTimer = millis();
        break;
  }
}

Some of the rest of the sketch should be re-arranged to get the delays out.

BTW, in small memory environments you should avoid using String variables.

That looks like a nice general solution from @GoForSmoke for handling a list of actions with with a definable time interval between each action. It also matches the behaviour of the existing code, with the exception, of course, that the function sms() must now be called multiple times.

However, if the OP wants to test the return code from each command and immediately continue with the next command as soon as he gets the expected return code, then he has more work to do.

I don't have the hardware with me but I understood both of your suggestions and will test it later after lunch. Thank you very much.

My attention was mainly on the InitModem function, as occasionally I'm getting "ERROR" on the Serial Monitor after issuing a command. However, everything works in the end, so maybe it's the delays causing trouble regarding when data is displayed not so much regarding it being accepted.

At some point I was trying this but I was never able to catch the "OK" not the end LED with turn on.

void InitModem() 
{
    mySerial.print("AT+CMGD=1,4\n\r");        
    ReadChar();
    if (val.indexOf("+OK") > -1) 
    {
        mySerial.print("AT+CMGF=1\n\r");
        ReadChar();
        if (val.indexOf("+OK") > -1) 
        {
            mySerial.print("AT+CSCS=\"GSM\"\r");
            ReadChar();
            if (val.indexOf("+OK") > -1) 
            {
                mySerial.print("AT+CNMI=2,2,0,0,0\n\r");
                while (mySerial.available())
                ReadChar();
                if (val.indexOf("+OK") > -1) 
                {digitalWrite(greenLED, HIGH); // signal InitModem finished, modem ready to receive SMS
                    sms(String("Module Active"), String(master));
                    delay(2000);
                    startup_sequence = false;
                }
            }
        }
    }
}

void ReadChar() 
{
    while (mySerial.available())
    {
        ch = mySerial.read();
        val += char(ch);
        delay(50);
    }   
}

Maybe add a debug statement in ReadChar() to see what you get and check all the conversions are working as you expect ( decimal, character, String etc.)

void ReadChar()
{
  while (mySerial.available())
  {
    ch = mySerial.read();
    val += char(ch);
    delay(50);  
  }
  Serial.print( "\nval= " ) ;
  Serial.println( val ) ;
}

You might also try val = mySerial.readString() instead of your loop, because this waits a certain amount of time, set by mySerial.setTimeout() but defaults to 1000mS , to collect data before returning.

6v6gt:
However, if the OP wants to test the return code from each command and immediately continue with the next command as soon as he gets the expected return code, then he has more work to do.

I would strongly recommend against integrating the serial input code and the serial output code. It may put the operations in order when you can do just one thing at a time with delays but it would make no-delay operating into a mass of spaghetti code.

The input code should run in every loop() by itself to receive serial at any time, buffer or process the char and set status for the output code to pick up on. The output code should run all the time to act on serial input. Neither should be inside the other.

The many do-many-things-at-the-same-time code tutorials give us techniques to do these things in small examples. Learning those also helps see how they go together. Working through some of those can help the coder with bigger projects.

I’m not sure I fully understand this.

Making one action, in this case sending an AT command to an external device, dependent on the results of a previous operation is quite a reasonable thing to do. If a failure is detected for example with the initialisation, then it is not useful to proceed to send the following commands.

Neither do I believe that spaghetti code is a inevitable consequence of attempting to solve this. A state machine approach could provide an elegant solution here. If required, some of the state transitions could have a timing element to allow, for example, a serial buffer to fill.

6v6gt:
Making one action, in this case sending an AT command to an external device, dependent on the results of a previous operation is quite a reasonable thing to do. If a failure is detected for example with the initialisation, then it is not useful to proceed to send the following commands.

Neither do I believe that spaghetti code is a inevitable consequence of attempting to solve this. A state machine approach could provide an elegant solution here. If required, some of the state transitions could have a timing element to allow, for example, a serial buffer to fill.

That depends on how that dependency is coded. If it is weaved into deeply indented condition-dependent code then it will take more code than small tasks would to do the same thing. The bigger the job, the more extra it will take. I know this from experience and working my way out. When you take fixed contracts, you don’t stick with the hard way.

State machines are good, they were part of the path up and out for me.

IPO tasking takes less code and may use state machines. It is a hell of a lot easier to add to.

Please note that the function I modified only works if called frequently during the time it runs. It maintains dependency but mainly not through indented structures though it does use indented structure… being C code.

I had to deal with an health issue in the family, hence not being able to give this thread more attention.

I have now successfully tested GoForSmoke’s code. I will test 6v6gt’s, then implement in my project.

I still need to read more about Serial communication and using C language char arrays (instead of the String library), but meanwhile I’ll share code that is working as it is.

Here’s a small sketch to send an SMS through the M590E GSM module by pressing a button and without using delays:

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 4); // RX, TX

const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  9;      // the number of the LED pin

int buttonState = 0;         // variable for reading the pushbutton status

String text = "button send"; // type message to be sent
String phone = "+351xxxYYYzzz"; // receiver's number

boolean triggerSMS = false;

void setup() {

  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);

  // Open serial communications and wait for port to open:
  Serial.begin(38400);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("Goodnight moon!");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(38400);
  mySerial.println("Hello, world?");
  
}

void loop() {

  // reads back and displays the response to the AT commands sent through the Serial Monitor
  if (mySerial.available()) {
    Serial.write(mySerial.read());
  }
  if (Serial.available()) {
    mySerial.write(Serial.read());
  }

  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
    triggerSMS = true; // button was pressed, send standard sms
  }

  if (triggerSMS == true) {
    Send( text, phone);
  }
}

void Send(String text, String phone) {

  static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
  static byte procState;

  if ( waitTimer > 0 )
  {
    if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
    else waitTimer = 0; // and on to the switch-case
  }

  switch ( procState )
  {
    case 0 :
      mySerial.println("AT+CMGF=1"); // Set SMS Text Mode
      procState = 1;      //  run case 1 next time
      waitTimer = 2000;
      startTimer = millis();
      break;

    case 1 :
      mySerial.println("AT+CSCS=\"GSM\"");  // 
      procState = 2;      //  run case 2 next time
      waitTimer = 2000;
      startTimer = millis();
      break;

    case 2 :
      mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
      procState = 3;      //  run case 3 next time
      waitTimer = 2000;
      startTimer = millis();
      break;

    case 3 :
      mySerial.print("\"" + text + "\""); // containing desired text
      procState = 4;      //  run case 4 next time
      waitTimer = 2000;
      startTimer = millis();
      break;

    case 4 :
      mySerial.print((char)26); // signals end of text message
      procState = 0;      //  run case 0 next time
      waitTimer = 2000;
      startTimer = millis();
      triggerSMS = false;
      break;
  }
}

Initiate the modem with a button press. SMS notification is sent upon completion of process. No delays.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 4); // RX, TX

const int buttonPin = 2;     // the number of the pushbutton pin
int redLED = 9; // extra LED for testing
int greenLED = 7; // ON when startup procedure is over // modem is ready to handle text messages

int buttonState = 0;         // variable for reading the pushbutton status

String text = "Module Active"; // type message to be sent
String phone = "+351xxxYYYzzz"; // receiver's number

boolean triggerInitModem = false;
boolean triggerSMS = false;

void setup() {
	pinMode(greenLED, OUTPUT);
	pinMode(redLED, OUTPUT);
	pinMode(buttonPin, INPUT_PULLUP);
	// Open serial communications and wait for port to open:
	Serial.begin(38400);
	while (!Serial) {
		; // wait for serial port to connect. Needed for native USB port only
	}
	Serial.println("Goodnight moon!");
	// set the data rate for the SoftwareSerial port
	mySerial.begin(38400);
	mySerial.println("Hello, world?");
}

void loop() {
	// reads back and displays the response to the AT commands sent through the Serial Monitor
	if (mySerial.available()) {
		Serial.write(mySerial.read());
	}
	if (Serial.available()) {
		mySerial.write(Serial.read());
	}
	
	// read the state of the pushbutton value:
	buttonState = digitalRead(buttonPin);
	// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
	if (buttonState == HIGH) {
		// turn LED on:
		digitalWrite(redLED, HIGH);
	} else {
		// turn LED off:
		digitalWrite(redLED, LOW);
		triggerInitModem = true; // button was pressed, send standard sms
	}
	if (triggerInitModem == true) {
		InitModem();
	}
	if (triggerSMS == true) {
		sms(text, phone);
	}
}
void sms(String text, String phone) {
	static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
	static byte procState;
	if ( waitTimer > 0 )
	{
		if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
		else waitTimer = 0; // and on to the switch-case
	}
	switch ( procState )
	{
	case 0 :
		mySerial.println("AT+CMGF=1"); // Set SMS Text Mode
		procState = 1;      //  run case 1 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 1 :
		mySerial.println("AT+CSCS=\"GSM\"");  //
		procState = 2;      //  run case 2 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 2 :
		mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
		procState = 3;      //  run case 3 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 3 :
		mySerial.print("\"" + text + "\""); // containing desired text
		procState = 4;      //  run case 4 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 4 :
		mySerial.print((char)26); // signals end of text message
		procState = 0;      //  run case 0 next time
		waitTimer = 2000;
		startTimer = millis();
		triggerSMS = false;
		break;
	}
}

void InitModem() {
	static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
	static byte procState;
	if ( waitTimer > 0 )
	{
		if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
		else waitTimer = 0; // and on to the switch-case
	}
	switch ( procState )
	{
	case 0 :
		mySerial.println("AT+CMGD=1,4");        // Delete all messages so there are no conflicts with accidentally unread or stored SMS
		procState = 1;      //  run case 1 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 1 :
		mySerial.println("AT+CMGF=1");          // Set the module message mode, set either at 0 or 1 : 0 = PDU / 1 = Text mode.
		procState = 2;      //  run case 2 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 2 :
		mySerial.println("AT+CSCS=\"GSM\"");      // Set the character sets : “GSM” = default alphabet (GSM03.38.6.2.1)
		procState = 3;      //  run case 3 next time
		waitTimer = 2000;
		startTimer = millis();
		break;
	case 3 :
		mySerial.println("AT+CNMI=2,2,0,0,0");  // Set message indication Format, AT+CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]]
		procState = 0;      //  run case 4 next time
		waitTimer = 2000;
		startTimer = millis();
		digitalWrite(greenLED, HIGH); // signal InitModem finished, modem ready to receive commands
		triggerSMS = true;
		triggerInitModem = false;
		break;
	}
}

I’ve modified my sketch to make it less verbose and added GoForSmoke’s routines.

Upon powering the device, the modem boots until it is able to receive commands, then receives commands to make it ready to handle text messages and notifies the user when it is ready. Light an LED by texting “ON” and vice-versa. Send “Temp” to receive the word “DHT11”, which in the future will be the temperature and humidity values from a DHT11 module.

I will try to include a sleep mode, low battery alert and use a ultrasonic anemometer as sensor.

Your suggestions will be appreciated.

/*-----------------------------------------------------------------------------------------
  Modified version of http://arduino.ru/forum/apparatnye-voprosy/datchiki-temperatury-ds18b20
*******************************************************************************************
  Light LED with SMS "On" and vice-versa, "Temp" to receive SMS with the word "DHT11"
  -------------------------------------------------------------------------------------------
  M590E GSM Module working voltage always above 3.5V and under 4.2V
  Use 3.3V level-shifter for Tx/Rx when using 5V Arduino
  Short Gnd and Boot pins to auto-boot on power ON
  -----------------------------------------------------------------------------------------*/
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 4); // RX, TX

int greenLED = 7; // ON when startup procedure is over // modem is ready to handle text messages
int yellowLED = 8; // SMS triggered with "On" or "Off"
int redLED = 9; // extra LED for testing

// hold what's available through the serial port
int ch = 0;
String val = "";

String phone = "+351xxxYYYzzz"; // replace with sender phone number
String text = "";

const unsigned long baudrate = 38400;  // baudrate for both Hardware Serial and Software Serial

boolean triggerInitModem = false;
boolean triggerSMS = false;

void setup() {
  mySerial.begin(baudrate);  // Set the baudrate of the GSM/GPRS Module.
  Serial.begin(baudrate);    // Set the baudrate of the serial monitor, start serial port at 38400 bps

  pinMode(greenLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(redLED, OUTPUT);
}

void loop() {
  if (mySerial.available()) // listen to the module
  {
    ReadChar(); // read and hold incoming data

    if (val.indexOf("+PBREADY") > -1) // modem is ready to receive AT commands to enable handling text messages
    {
      triggerInitModem = true;
    }
    if (val.indexOf("+CMT") > -1) // an SMS has been received
    {
      if (val.indexOf(phone) > -1) // by a known phone number
      {
        handleSMS(); // do something according to text message content
      }
    }
    val = ""; // clear what val was holding and let's listen for more
  }

  if (triggerInitModem == true) InitModem();
  if (triggerSMS == true) SendSMS(text, phone);
}

void ReadChar() {
  while (mySerial.available()) {
    ch = mySerial.read();
    val += char(ch);
    delay(1);
    Serial.println(val);
  }
}

void handleSMS() {
  if (val.indexOf("On") > -1)
  {
    digitalWrite(redLED, HIGH);
    text = "redLED ON";
    triggerSMS = true;
  }
  if (val.indexOf("Off") > -1)
  {
    digitalWrite(redLED, LOW);
    text = "redLED OFF";
    triggerSMS = true;
  }
  if (val.indexOf("Temp") > -1)
  {
    text = "DHT11";
    triggerSMS = true;
  }
}

void InitModem() {
  static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
  static byte procState;
  if ( waitTimer > 0 )
  {
    if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
    else waitTimer = 0; // and on to the switch-case
  }
  switch ( procState )
  {

    case 0 :
      mySerial.println("AT+CMGF=1");          // Set the module message mode, set either at 0 or 1 : 0 = PDU / 1 = Text mode.
      procState = 1;      //  run case 2 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 1 :
      mySerial.println("AT+CSCS=\"GSM\"");      // Set the character sets : “GSM” = default alphabet (GSM03.38.6.2.1)
      procState = 2;      //  run case 3 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 2 :
      mySerial.println("AT+CMGD=1,4");        // Delete all messages so there are no conflicts with accidentally unread or stored SMS
      procState = 3;      //  run case 1 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 3 :
      mySerial.println("AT+CNMI=2,2,0,0,0");  // Set message indication Format, AT+CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]]
      procState = 0;      //  run case 4 next time
      waitTimer = 2000;
      startTimer = millis();
      digitalWrite(greenLED, HIGH); // signal InitModem finished, modem ready to receive commands
      text = "Module Active";
      triggerSMS = true;
      triggerInitModem = false;
      break;
  }
}

void SendSMS(String text, String phone) {
  static word startTimer, waitTimer;  // waitTimer > 0 causes the timer to run, good up to 65 second intervals
  static byte procState;
  if ( waitTimer > 0 )
  {
    if ( word( millis() ) - startTimer < waitTimer )      return;     // time is not up yet
    else waitTimer = 0; // and on to the switch-case
  }
  switch ( procState )
  {
    case 0 :
      mySerial.println("AT+CMGF=1"); // Set SMS Text Mode
      procState = 1;      //  run case 1 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 1 :
      mySerial.println("AT+CSCS=\"GSM\"");  //
      procState = 2;      //  run case 2 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 2 :
      mySerial.println("AT+CMGS=\"" + phone + "\"");  // send to desired phone
      procState = 3;      //  run case 3 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 3 :
      mySerial.print("\"" + text + "\""); // containing desired text
      procState = 4;      //  run case 4 next time
      waitTimer = 2000;
      startTimer = millis();
      break;
    case 4 :
      mySerial.print((char)26); // signals end of text message
      procState = 0;      //  run case 0 next time
      waitTimer = 2000;
      startTimer = millis();
      triggerSMS = false;
      break;
  }
}

I hope you can apply that trick to other code? You change the cases.

You can do so much more with switch case. The cases do NOT have to be run in order nor end by setting the timer. Do ya like the one timer for all those cases? When reading serial chars into a buffer as they arrive (many cycles apart), one case might run over and over until the buffer is full or a newline is read and -then- change to the next case that does the next thing.

When your code takes events and time into its design, it becomes real world code. If it's good then it's good real world code, suitable for automation.

Your code was easy to understand, adapt and implement. I have saved it and will use it more in the future to get rid of delay(), for example.

But you are right, switch cases can be very powerful and they have helped me in the past with momentary switches:

https://github.com/marianopw/multiple-buttons-with-multiple-actions/blob/master/Multiple-buttons-singlepress-dobleclick-hold-longhold.ino

I was lucky to find this code in the forum and nowadays I use it to handle my buttons in most projects. The remote controller I use with my radio control gliders only has 3 buttons but each button has 4 functions thanks to this code.

How many control surfaces does the glider have? I've seen some that have only pitch and yaw, use coupling for roll.

Only two, Elevator and Rudder, controlled by a joystick. The buttons help to finely adjust the deflection of the surfaces in order to achieve a stable launch and glide. Perfect positioning is lost when building these models by hand. The buttons also adjust the maximum deflection allowed and the mix of movement of the two servos when flying a delta wing (flying surfaces initially on the same geometrical plane instead of perpendicular like in a traditional glider).

https://www.youtube.com/watch?v=oklWCgm2OoQ

https://www.youtube.com/watch?v=mOz5UdqamH4

Ailerons would be cool. You could make coordinated turns.

I have made small flying wing gliders in the past. They're allergic to wind.

The code I'm running allows that but I do not own any planes with Ailerons. However, I use this transmitter with a small helicopter and "drones" which have throttle, pitch, yaw and roll.

If you are interested, here's the author's site:

http://www.mccrash-racing.co.uk/sc/archive.htm

(articles P16 and P17)