Using yield () within scheduled loops

Hi Guys,

I am working with the scheduler class and the yield() function and I am having issues. I am working with an Arduino Due and the scheduler library is https://github.com/arduino-libraries/Scheduler. I am hoping you guys might be able to shed some light on it. I will give an explanation of my issue and post stripped down code below. I removed some commands with passwords etc. but left commands in there place that would cause the same actions to occur.

As an overview I am trying to write some code that resets and re initializes a 3G module if it has lost its connection for a long period of time. The issue is that some of the 3G module commands can take up to three minutes to reply and I need to confirm the reply before sending the next command. With all the commands I need to send to reset and re initialize the wait time would be huge and I can’t afford to delay my main code at all.

To combat this I have added a separate schedule for the reset function. Inside this schedule if retry3G is set (set in main when I need to retry) I trigger a call to a reconnect3G function. Reconnect3G calls one other function (send3gCommand) which is where I hold the code and check the serial2 waiting from a reply.

I need to use yield() in a way that I can return control to my main loop while still executing the second scheduled loop correctly but I am not sure how to do this. I think I need to place a yield() inside the send3gCommand function inside the while (timeToConstructReply <= timeout) loop but I just wanted some advice. Yield() is completely new to me and I'm finding it hard to find info on it.

Thanks for reading :slight_smile:

//inside setup 

Scheduler.startLoop(Three_G_Loop);



void Three_G_Loop() 
{
 if (retry3G)
 {
 reconnect3G();
 }
 
 yield(); //not working from here
}

void reconnect3G()
{
 
 send3gCommand ("AT+CFUN=16", 180000, "OK",1, 0);    // send3gCommand (Command to send, timeout, text to find in reply for success, attempts, delay between retrys); 
 
 send3gCommand ("ATz", 100, "ATzOK", 15, 200); 
 
 send3gCommand ("AT+CMEE=2", 200, "AT+CMEE=2OK", 1, 0);
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 delay(1000); 

 send3gCommand ("AT+UPSDA=0,3", 180000, "AT+UPSDA=0,3OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 

 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 
 
 send3gCommand ("AT+CMD", 200, "OK", 1, 0); 

 retry3G = 0;
 commsModuleFailTimeout = 0; 
 
}

bool send3gCommand (String cmd, int timeout, String text, int attempts, int delayRetry)
{
 int temp =0;
 int msgMax = 0;
 int errorMsgMax = 0;
 String errorText ="ERROR";
 String commandReply = "";
 elapsedMillis timeToConstructReply;

 while (attempts > 0)
 {
 //Send command
 Serial2.println(cmd);
 
 while (timeToConstructReply <= timeout) 
 { 
 //Construct reply 
 if (Serial2.available() >0) 
 {
 temp = Serial2.read();

 if (temp != '\r' && temp != '\n' && temp != ' ') 
 {
 char convertedChar = char(temp); 
 commandReply += convertedChar;
 }
 }
 
 //Check reply for text for success once large enough
 if ((commandReply.length()) >= (text.length()) ) 
 {
 msgMax = commandReply.length() - text.length();

 for (int i = 0; i <= msgMax; i++) 
 {
 if (commandReply.substring(i, (i+text.length())) == text)
 {
 delay(20); 
 clearRxSerial2Buff();
 return true;
 }
 }
 }

 //Check the reply text for error for early exit
 if ((commandReply.length()) >= (errorText.length()));
 {
 errorMsgMax = commandReply.length() - errorText.length();

 for (int i = 0; i <= errorMsgMax; i++)
 {
 
 if (commandReply.substring(i,(i + (errorText.length()))) == errorText) 
 {
 Serial.println("IN ERROR");
 timeToConstructReply = timeout+1; 
 }
 }
 }
 }
 
 //All attemps failed, clear serial and reset everything we use for next attempt
 clearRxSerial2Buff();
 delay (delayRetry);
 attempts-= 1;
 commandReply = "";
 timeToConstructReply=0;
 }
 return false;

}

void clearRxSerial2Buff() 
{
 while (Serial2.available()>0)
 {
 Serial2.read();
 }
}

Edit: Added board type and link to Library used

There are many scheduler libraries for the Arduino. Your text and your incomplete code do not tell us which one. A link to the library would be nice too.

Most (all?) schedulers do not work with the delay(...) function. delay(1000) wastes the considerable power of your Arduino for a whole second.

Hi vaj4088, apologies for not including the library details. I am working on an arduino Due and the scheduler library is

GitHub - arduino-libraries/Scheduler: Scheduler Library for Arduino.

vaj4088:
Most (all?) schedulers do not work with the delay(...) function. delay(1000) wastes the considerable power of your Arduino for a whole second.

Also thank you for the advice on the delay function. I done some testing initially on the scheduler and just made it call a function with a delay (150000) and it allowed the rest of my code to continue running along side.
That gave me the impression that delays would be fine to use but I do hate to use them. I only did so as this was initially only meant to be run on wake up during the setup loop where timing wasn't as much of an issue. I will modify the code and get rid of the use of delay function.

Surprisingly, that library works fine with delay(...). In fact, their examples use delay(...) so do not make any changes.

vaj4088:
Surprisingly, that library works fine with delay(...). In fact, their examples use delay(...) so do not make any changes.

Brilliant, thank you. I thought it worked alright but I was going to change just to be sure. Can I ask if you know how I can use yield correctly so that my Three_G_Loop actually yields as intended? Do I need to place yield inside any functions or loops that may hold the code. By this I mean should I have a yield inside reconnect3G, send3gCommand and also in any internal while loops of these? The examples and reference don't really go into the yield() function very much.

CyberOddity:
Brilliant, thank you. I thought it worked alright but I was going to change just to be sure. Can I ask if you know how I can use yield correctly so that my Three_G_Loop actually yields as intended? Do I need to place yield inside any functions or loops that may hold the code. By this I mean should I have a yield inside reconnect3G, send3gCommand and also in any internal while loops of these? The examples and reference don't really go into the yield() function very much.

The correct way to do it is to re-write the code so you don't spend long periods of time at ANY point in the code. Do that, and you don't need yield().

Regards,
Ray L.

I admit that I do not know that specific library.

For debugging, I would suggest that the function send3gCommand(...) should print what it is doing on a serial terminal.

This comment

//not working from here

is not useful but I suspect that the optimizer is getting you and that the variable retry3G needs to be declared as volatile. Just a WAG, it would do little harm to try it.

yield() allows another task or tasks (loop or loops that were previously set up) to execute. Use it wherever you are willing to allow something else to execute.

RayLivingston is correct. If you can do this, then you will not need yield(), in which case you will not need the scheduler library (which the documentation admits is experimental).

You should be able to keep track of the time using millis() (or micros() ) and avoid the need for the scheduler library. if you can keep track of the time, then you will not need delay(...) either.

vaj4088:
RayLivingston is correct. If you can do this, then you will not need yield(), in which case you will not need the scheduler library (which the documentation admits is experimental).

You should be able to keep track of the time using millis() (or micros() ) and avoid the need for the scheduler library. if you can keep track of the time, then you will not need delay(...) either.

Depending on the application requirements, scheduler can still be useful, as it does pre-emptive multi-tasking, so tasks requiring low-latency can be serviced deterministically, provided there are no long, blocking functions.

Regards,
Ray L.

RayLivingston:
The correct way to do it is to re-write the code so you don't spend long periods of time at ANY point in the code. Do that, and you don't need yield().

Regards,
Ray L.

Hi Ray, thanks for taking the time to read and reply. I completely agree that I should not stay in the code for prolonged periods of time but unfortunately I'm working on code written by someone else that is extremely complex and limits certain things I can do. Also the way the 3G module works means I have to wait for replies (which can vary based on signal strength etc) from the previous command before I can send the next. The code already uses the scheduler library to run a few other loops so using it seemed logical to use this.

Hopefully I can get the code working using this scheduler and then I will go back to the drawing board and try to generate new ideas of ways to avoid using this. Thanks again.

If anyone else reading has any suggestions on the placement of yield() it would be greatly appreciated