Arduino "multitasking"

Hi,
I am making a GSM alarm system. When a sensor is triggered, it waits for a keyboard input, and if a correct code isn't entered, it sounds an alarm and sends an SMS to 5 phone numbers. The problem is, that sending messages takes up to 10 seconds ( :o ), and during that, the user can't stop the alarm... = I need the user to be able to cancel the alarm while the GSM shield is sending the messages. I know microcontrollers cant multitask, but I think there must be a way around this..

Thanks for any help in advance..

anno9:
I know microcontrollers cant multitask

Wrong.

The Arduino can only perform tasks in successive steps, not at the same time. if you want the user to be able to disable the alarm which waiting for the messages to be sent, maybe try using an interrupt?

Bill

Billdefish:
The Arduino can only perform tasks in successive steps, not at the same time. if you want the user to be able to disable the alarm which waiting for the messages to be sent, maybe try using an interrupt?

Bill

Yes, but those successive steps can be mere microseconds apart, and organized to be concurrent for all practical purposes. An interrupt is probably not needed for this. Cooperative multitasking is old hat on the Arduino.
More than one thing at a time

While not a highly advanced topic, achieving what you want requires a strong knowledge of the hardware and software you are using... and the underlying principles in general.

My current project uses time-slicing to handle multiple unrelated tasks concurrently - with the knowledge that some take far longer than others. It took a lot of time to refine and debug. Just FYI, I'm self-taught, but just put the effort in over the last thirty years while working in a different field (!). That's unfortunately what it takes to learn. This wasn't written in a weekend.

__For SMS, I create a 'queue' of outgoing messages, and on the next pass of the slicer, a separate function looks to see if the previous message has gone, and if there are any more in the queue - then sends that next. And so on, without holding up faster tasks. __

This is tied in with a serial console, SMS receive and parser, contact-closure inputs with programmable delays, front panel push buttons, timed/pulsed relay outputs, LCD and LED indicator panels... recurring daily timer events, and many other functions that all happen concurrently when they're supposed to with sub-second resolution.

There is very little linear, sequential code, and is currently over 3000 lines (without libraries). ATMEGA-1284P FLASH is half full, SRAM about 80%, EEPROM maybe 20% with non-volatile user-adjustable values.
p.s. The only delay() calls are during power-on initialisation!

Hiya anno9,

So reading the responses, I gather that you now get that this can be done. But how?

Read the thread called "Demonstration code for several things at the same time". It's really straight forward (if a bit long.) Once you understand this sketch, you will be able to do exactly what the thread title says.

anno9:
Hi,
I am making a GSM alarm system. When a sensor is triggered, it waits for a keyboard input, and if a correct code isn't entered, it sounds an alarm and sends an SMS to 5 phone numbers. The problem is, that sending messages takes up to 10 seconds ( :o ), and during that, the user can't stop the alarm... = I need the user to be able to cancel the alarm while the GSM shield is sending the messages. I know microcontrollers cant multitask, but I think there must be a way around this..

Thanks for any help in advance..

The problem is, that sending messages takes up to 10 seconds

This, of course, is the core of the problem but we don't know how the SMS is being sent and whether the code or library function used to do it is blocking, which it sounds like it is.

OP - please post your code as it is now.

Hi,
thanks for all your help.
This is my shortened code..

//Al there is to loop
void loop()
{
  if(checkKeyboard() || alarmState > 3)
  {
    changeAlarmState();
  }
  if(alarmState == 2)
  {
    if(!checkZones())
    {
      entryTime(getActiveZone());
    }
  }
  if(millis() - GSMpreviousMillis > 20000)
  {  
    checkGSMConnected();
    GSMpreviousMillis = millis();
  }
}

//I didn't include other functions, as they arent the source of problem

//
void entryTime(int activeZone)
{
  Serial.println(F("Objekt narusen, spoustim prichodovy cas!"));
  unsigned long previousMillis = millis();
  while(millis() - previousMillis <= entryTimeVal*1000){  
    if (checkKeyboard() == 1){
      Canceled();
      return;  
    }
    tone(outputBuzzer,1100);
    leds(1,1); 
    delay(200);
    noTone(outputBuzzer);
    leds(0,1);   
    delay(200);
  }  
  //if no keyboard input
  Serial.println(F("Alarm aktiviovan!"));
  tone(outputBuzzer, 1100);
  leds(0,1);
  alarmState = 3;
  alarm(1);
  sendSMStoAll(String(F("Objekt narusen, pricina: zona ")) + activeZone + " " + loadZoneName(activeZone)); 
  // /\ this blocks execution for about 10 s, while the user cant cancel the alarm
}

//the multiple SMS sending function
void sendSMStoAll(String text)
{
  char textSMS[160];
  text.toCharArray(textSMS, 160);
  char phoneNum[20];
  for(int i = 0; i < 5; i++)
  {
    loadPhoneNumber(i+1).toCharArray(phoneNum,20);
    if(sms.SendSMS(phoneNum , textSMS))
    {
    Serial.println("SMS odeslana!");
    }
  }
}



//But this fatty is also a problem (up to 10s)
void checkGSMConnected()
{
    ledsGSM(1,1);
    boolean GSMregistered = gsm.CheckRegistration();
    if(AT_RESP_OK == gsm.SendATCmdWaitResp(str_at, 500, 100, str_ok, 5)) //this alone takes about a second
    { 
      Serial.println(F("GSM OK."));
      char sms_position = sms.IsSMSPresent(SMS_UNREAD);
 
      if (sms_position)
      {
        char phone_number[20];
        char sms_textCH[160];
        Serial.println(F("\nPrijata SMS:"));
        sms.GetSMS(sms_position, phone_number, sms_textCH, 100);
        Serial.println(phone_number);
        Serial.println(sms_textCH); 
        String sms_text = String(sms_textCH);
        sms_text.toLowerCase();
        if(sms_text.indexOf(pinSMS) > -1)
        {
          Serial.println(F("PIN OK!"));
          String reply = "Nebyl rozpoznán příkaz!";
          if(sms_text.indexOf("entrytime") > -1) // I don't really know how to make those shorter, or less resource consuming
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("entrytime", "");
            EEPROM.update(3, sms_text.toInt());
            entryTimeVal = sms_text.toInt();
            reply = String(F("Prichodovy cas nastaven na ")) + entryTimeVal + " sekund!";
          } else
          if(sms_text.indexOf("exittime") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("exittime", "");
            EEPROM.update(4, sms_text.toInt());
            exitTimeVal = sms_text.toInt();
            reply = String(F("Odchodovy cas nastaven na ")) + exitTimeVal + " sekund!";
          } else
          if(sms_text.indexOf("pin") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("pin", "");
            sms_text.trim();
            savePin(sms_text);
            pinSMS = sms_text;
            reply = String(F("SMS PIN nastaven na ")) + pinSMS;
          } else
          if(sms_text.indexOf("output1") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("output1", "");
            sms_text.trim();
            digitalWrite(outputGSM1, sms_text.toInt());
            reply = String(F("GSM vystup 1 nastaven na ")) + digitalRead(outputGSM1);
          } else
          if(sms_text.indexOf("output2") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("output2", "");
            sms_text.trim();
            digitalWrite(outputGSM2, sms_text.toInt());
            reply = String(F("GSM vystup 2 nastaven na ")) + digitalRead(outputGSM2);
          } else
          if(sms_text.indexOf("setzonename") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("setzonename", "");
            sms_text.trim();
            int zoneNum = String(sms_text.charAt(0)).toInt();
            sms_text.replace(String(zoneNum),"");
            saveZoneName(zoneNum, sms_text); 
            reply = String(F("Nazev zony ")) + zoneNum + " nastaven na "  + loadZoneName(zoneNum);
          } else
          if(sms_text.indexOf("getzonename") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("getzonename", "");
            sms_text.trim();
            int zoneNum = String(sms_text.charAt(0)).toInt(); 
            reply = String(F("Nazev zony ")) + zoneNum + " je "  + loadZoneName(zoneNum);
          } else
          if(sms_text.indexOf("setphonenumber") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("setphonenumber", "");
            sms_text.trim();
            int orderNum = String(sms_text.charAt(0)).toInt();
            sms_text.replace(String(orderNum),"");
            savePhoneNumber(orderNum, sms_text); 
            reply = String(F("Tel. cislo na pozici ")) + orderNum + " nastaveno na "  + loadPhoneNumber(orderNum);
          } else
          if(sms_text.indexOf("getphonenumber") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("getphonenumber", "");
            sms_text.trim();
            int orderNum = String(sms_text.charAt(0)).toInt();
            reply = String(F("Tel. cislo na pozici ")) + orderNum + " je "  + loadPhoneNumber(orderNum);
          } else
          if(sms_text.indexOf("setstate") > -1)
          {
            sms_text.replace(pinSMS, "");
            sms_text.replace("setstate", "");
            sms_text.trim();
            alarmState = sms_text.toInt();
            reply = String(F("Stav alarmu byl zmenen na ")) + alarmState;
          } else
          if(sms_text.indexOf("getstate") > -1)
          {
            reply = String(F("Stav alarmu: ")) + alarmState + ", GSM1 ve stavu " + digitalRead(outputGSM1) + ", GSM2 ve stavu " + digitalRead(outputGSM2) + ", prichodovy/odchodovy cas " + entryTimeVal + "/" + exitTimeVal + "."; ;
          } else
          if(sms_text.indexOf("getsensorstates") > -1)
          {
            reply = String(F("Stav senzoru: ")) + alarmState + ", 1: " + analogRead(alarmZone1) + ", 2: " + analogRead(alarmZone2) + ", 3: " + analogRead(alarmZone3) + ", 4: " + analogRead(alarmZone4);
          }
          char textSMS[160];
          reply.toCharArray(textSMS, 160);
          sms.SendSMS(phone_number, textSMS);
          Serial.println(reply);
        }
        else
        {
          Serial.println(F("SMS zprava neobsahuje spravny PIN!"));
        }
        sms.DeleteSMS(sms_position);
        Serial.println("Smazano");
      } else {    
        Serial.println(F("Nebyla prijata zadna SMS"));
      }      
    }else{
      Serial.println(F("GSM bylo odpojeno, pripojuji..."));
      ledsGSM(0,1);
      if (gsm.begin(4800)) 
      {
        Serial.println(F("GSM pripraveno!"));
        ledsGSM(1,0);
      }else{
        Serial.println(F("GSM nepripojeno!"));
      }
    
    }
    ledsGSM(1,0);
}

anno9:
thanks for all your help.
This is my shortened code..

Does this mean it is now working OK?

ChrisTenone:
It's really straight forward (if a bit long.)

It is only necessary to read the first 2 posts in the Thread :slight_smile:

...R

No, the problem is still there unfortunately :confused: I showed you the code so you can help me shorten the delay/solve the problem in another way.

This is my shortened code..

All of it would be better

As I said, you dont need other parts of my code, because they arent important.. At least for the blocking code of the GSM module..

As I said, you dont need other parts of my code,

I think you have had all the answer you are going to get. The rules require you to post ALL your code. Don't argue - just doit.

And not I'm not going to waste my time telling you why.

Mark

OK then, I'm not going to waste time posting on the forum again. Consider this topic closed.

The easy way to reduce the delay is checking for a keypress between each SMS; if 2 seconds is still too long, you probably have to modify the gsm library.

One simple solution for this, is to use two microcontrollers, with i2c or SPI communication between them, or perhaps even just signalling each other using single pins.

That way you can let the one responsible for sending SMS block as much as it wants, and have another take care of user input. Should be much easier than potentially having to rewrite the SMS-library to be fine-grained enough for some cooperative multitasking scheme to have the desired effect.

Rupert909:
One simple solution for this, is to use two microcontrollers, with i2c or SPI communication between them, or perhaps even just signalling each other using single pins.

That way you can let the one responsible for sending SMS block as much as it wants, and have another take care of user input. Should be much easier than potentially having to rewrite the SMS-library to be fine-grained enough for some cooperative multitasking scheme to have the desired effect.

...and once it gets the user input to cancel, how would it cause a call cancellation on the other microcontroller?

anno9:
OK then, I'm not going to waste time posting on the forum again. Consider this topic closed.

Well, that didn't take long.

I am always amazed by people who want help and then think they know more than the people they expect help from. :slight_smile:

...R

holmes4:
I think you have had all the answer you are going to get. The rules require you to post ALL your code. Don't argue - just doit.

And not I'm not going to waste my time telling you why.

Mark

And I'm always amazed that folks have to be so rude to folks.

If someone doesn't want their time wasted, why do they bother spending so much time telling folks not to waste their time?

"The rules" should also require that people be cordial.

aarg:
...and once it gets the user input to cancel, how would it cause a call cancellation on the other microcontroller?

The original post said nothing about cancelling message sending in progress, only being responsive to the user in the time spent sending SMS.

"I need the user to be able to cancel the alarm while the GSM shield is sending the messages"

If the user enters the correct code while the sending is in progress, it can signal this by raising or lowering a pin, which the other microcontroller can check inbetween sending each SMS.

The key of my suggestion is to avoid having to rewrite GSM library code.

:slight_smile: