Break from while loops after specific time

Hy,
I have a long code with multiple while statements for serial / i2c read (do{.....}while).
My question is what would be the most rational solution to break those loops in case of error. So lets say if loop doesnt break by itself after a second, break the loop.

I know that solution would be millis(), but for example like with blink without delay millis starts counting on power on. I would need millis to start counting when while loop starts running. Also because I have multiple loops I would like to use same values (char / int / anything else) for all the loops.

Or should I do the long way and do it like:

char counter = 0;

do{
counter++
if(counter == 1)
{
millis() = 0;
}
//do stuff
}while (.... && millis() < 1000)

couter = 0;

It could be a case for a goto.
Hard to say on the limited evidence.

If your Arduino board has a lot to do and is waiting in a while-loop for 99% of the time, that would not be efficient.

It is better to use millis-timers to do things, instead of hanging around in while-loops.

To get out of while-loops or do-while-loops, a variable or a break is used.

bool running = true;
while(running)
{
  if(...)
    running = false;
}

while(true)
{
  if(...)
    break;
}

A millis-timer in a while-loop does not make sense. In that case, a delay() would be better. Can you show a small sketch that shows the problem ?
Millis-timers are versatile. The interval can be changed while it is running; It can be turned on and off in other parts of the code; It can blink with a pattern; and so on.
I made a few basic examples: Fun with millis

Do you set the clock in your house to twelve o'clock when you boil an egg :smiley: No, you don't. You look at the clock, note down the time and check the clock regularly to see if the desired time has lapsed.

Below will need some fine tuning but (hopefully) shows the approach

  char counter = 0;
  uint32_t startTime;

  do {
    counter++
    if (counter == 1)
    {
      startTime = millis();
    }
    //do stuff
  } while (.... && (millis() - startTime < 1000));

  couter = 0;

very compact description of the basic principle

If the i2c-device does not answer the i2c-routine will hang. i2c-communication is blocking. If this device is a professional build device or a professional build i2c-chip its i2c-bus will work very reliable. If it is something hand-coded for communication between two microcontrollers I would change this communication to serial, because serial can be configured with timeouts.

best regards Stefan

unsigned long stop = millis();
while(millis()-stop<1000&& whatever){
i'm doing whatever for 1 second

}

this works but again creates a partwise blocking code through the while-loop.

As the name says while (condition evaluates to true)
stay INSIDE the while-loop

best regards Stefan

my code could be used as a timeout when a "speedy" response is expected or required for a program to continue to run. It just really depends on what he is doing. And I don't really know.

Hy, thank you for anwsers. I'm at work right now so I cant send the code. I can tell an example.
Problem is not really i2c part, but serial (chinesse mp3 board).
For example message that sends the filename always starts with MF+ and when I need filename arduino is sending MF+ command and reading the anwser until message that starts with MF+ gets returned.

That is just one example, then I have similar thing for total files on usb drive, number of playing file and so on...

Basicly I want to exit without a message as loop will execute again in a few ms if value wont change...

Basicly I need a timeout because every now and then whole thing freezers and I am almost sure it gets stuck in a while loop.

I will try taterkings idea as it is simple.

Yes, you are on the right track. Take a timestamp just before the loop starts and compare it against millis().

But it would be best to try and address this issue:

Would like to address this issue, but I have a feeling there is realy not much I can do. It is a chinesse board that "gets confused sometimes".
Idea is to send it for example MF+ command and the board should return ACK that looks like MF+filename...
I`m sending MF+ and reading the ACK until I get ACK that starts with MF+.

This should work in first try, but it is not always the case... sometimes in doesnt work at all Anyway same command will execute soon enough and worst case in finished product would be filename of the playing song refreshing few ms late.

Does this work only with declaring:
unsigned long stop = millis();
in setup or do I need to write:
stop = 0;
stop = millis();
before every while loop?

EDIT:
As and aftertought I think this is the way?
unsigned long stop = millis();
in the setup then

stop = millis();
before every while loop

Not necessarily.
If you are doing a whole series of transactions talking to say an external device and, if any of those transactions fail by getting stuck in a loop etc., you want to abandon the whole thing and start again, then this can be a use for "goto" as mentioned in post #2. You can set a timeout on the whole series. For example :

bool initiateSession() {
  const uint32_t timeoutMs = 4000 ;
  uint32_t startAtMs = millis() ;

  while ( true ) {
    if ( millis() - startAtMs > timeoutMs ) goto errorLabel ;
    /*
        do activity 1 here. when OK issue break
    */
  }

   while ( true ) {
    if ( millis() - startAtMs > timeoutMs ) goto errorLabel ;
    /*
        do activity 2 here. when OK issue break
    */
  }

  return true ;  // OK

errorLabel :
  // handle error here. Issue error message etc.
  return false ;  // bad
}

Hy, thank you for the idea but I want to continue loop like everything was fine actualy.

OK. Then it is probably something like this if you simply want to carry on following a timeout. Assuming the timeout is the same for each while loop:

bool initiateSession() {
  const uint32_t timeoutMs = 4000 ;
  uint32_t startAtMs  ;

  startAtMs = millis()
  while ( <condition1 not met> ) {
    if ( millis() - startAtMs > timeoutMs ) break ;
    /*
        do activity 1 here
    */
  }

  startAtMs = millis()
  while ( <condition2 not met> ) {
    if ( millis() - startAtMs > timeoutMs ) break ;
    /*
        do activity 2 here
    */
  }
  
  return true ;  // OK
}

I solved my main problem wich was arduino not sending pause command when radio gets turned off...
Stupid me... problem was with arduino hanging on wire.write...
Solution was to use Wire.setwiretimeout();
Explanation:
Arduino is writing to display via i2c, if I turned off radio while i2c transmission it would hang there....

I also added timeout code for while loops as taterking suggested... My project looks stable... for now.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.