1

The hardware is an Arduino Uno, using the Arduino IDE.
As a simplest working example, say I have this code (partially from Edgar Bonet's answer):

// Simulate an initialization that can hang. Connect pin 8 to ground if
// you want the function to ever return.
void initialize()
{
    pinMode(8, INPUT_PULLUP);
    while (digitalRead(8) == HIGH)
    ;  // wait
}

setup()
{
    initialize();             //Function I want to run

    Serial.begin(9600);       //Baud rate
    Serial.println("TEST 1"); //Test message
}

loop()
{
    Serial.println("TEST 2"); //Test message
    delay(1000);
}

if the initialize() function does not complete after some time, such as 2 secs., is there a way to ignore that function and continue with the rest of the code?

  • 2
    Not really. But you could use a watchdog timer to give up and start over; and you can detect that this happened and maybe skip that function. Or you can use a scheduler and have tasks take turns rather than run sequentially. Generally though, even if you use one of these things, you should make your functions deterministically conclude and explicitly handle the situations that might cause operations to get stuck. – Chris Stratton Apr 9 '18 at 4:06
  • An infinite busy loop, like you have coded, is not a great idea. Is there a reason you didn't try for(int l = 0; l < 20 && (digitalRead(8) == HIGH); ++l){Sleep(100);}? Not a brilliant solution, but pretty fool proof. – Code Gorilla Apr 10 '18 at 13:36
  • @CodeGorilla That initialize function was only there to try make the question more specific, since this question was "put on hold as too broad". The actual function I use comes from a library I didn't write, and I wanted to avoid changing it since it was a bit hard to follow and had several inter-related dependent files. – plu Apr 10 '18 at 22:22
2

In general, the right thing to do in such a situation is to fix the initialize() function so that it always returns. If there is a timeout to be handled, it should be handled within that function. This is likely to be the easiest and cleanest solution.

However, if for whatever reason that is not feasible (maybe it comes from a library that is too opaque to edit), then the idea suggested by Michel Keijzers is a viable alternative:

[...] set up a timer interrupt and start it just before you initialize the device/component you want to initialize. When the timer interrupt triggers and the device is not initialized, behave the same like a timeout has occurred.

The issue with this is that you would want the ISR (interrupt service routine) to return to the point where initialize() was called. This is not trivial, but it can be done. Actually, and contrary to what my own comment says, it can be handled without resorting to assembly hacks, by using the setjmp “non-local goto” feature of the avr-libc. Here is a proof of concept:

#include <setjmp.h>

jmp_buf env;

// Simulate an initialization that can hang. Connect pin 8 to ground if
// you want the function to ever return.
void initialize()
{
    pinMode(8, INPUT_PULLUP);
    while (digitalRead(8) == HIGH)
        ;  // wait
}

// This fires when the initialization times out.
ISR(TIMER1_OVF_vect)
{
    TIMSK1 = 0;       // disable timer interrupt
    longjmp(env, 1);  // go to where setjmp() was called
}

void setup()
{
    Serial.begin(9600);

    // Configure Timer 1 to fire an interrupt in about 4 seconds.
    TCCR1A = 0;           // reset timer
    TCCR1B = 0;
    TCNT1  = 0;
    TIFR1  = _BV(TOV1);   // clear interrupt flag
    TIMSK1 = _BV(TOIE1);  // enable timer overflow interrupt
    TCCR1B = _BV(CS10)    // clock at F_CPU / 1024
           | _BV(CS12);   // ditto

    // Attempt initialization.
    if (setjmp(env) == 0) {

        // This is the normal program flow.
        Serial.println("Attempting initialization.");
        initialize();
        TIMSK1 = 0;  // disable timer interrupt
        Serial.println("Initialization succeeded.");
    } else {

        // We get here through longjmp() in case of timeout.
        Serial.println("Initialization aborted on timeout.");
    }

    Serial.println("Moving on to the rest of the program.");
}

void loop(){}

If pin 8 is grounded, the initialization succeeds and the program prints

Attempting initialization.
Initialization succeeded.
Moving on to the rest of the program.

If pin 8 is left floating, the initialization hangs and the program prints

Attempting initialization.
Initialization aborted on timeout.
Moving on to the rest of the program.

with a 4 second pause between the first and second lines.

1

I'm not sure if you should want this in the first place. Initialization normally should contain the necessary parts to start with.

If there is a device/component you want to initialize that is not able to initialize (because it is not present, or broken), check first if there is an intiialization function with a timeout and when the timeout is reached, set a boolean to denote the device has not been initialized.

If there is not a timeout function, you could try to set up a timer interrupt and start it just before you initialize the device/component you want to initialize. When the timer interrupt triggers and the device is not initialized, behave the same like a timeout has occurred.

  • 1
    About your timer interrupt suggestion: Ideally, the ISR should return to the caller of initialize(), but that would be quite tricky, and would probably have to handled in assembly. If you can manage to add to your answer some example code implementing the idea, it would be really great. – Edgar Bonet Apr 9 '18 at 12:25
  • @EdgarBonet If it is problematic to have an ISR during initialize, why not create the initialization part that might fail in the loop() function and after initialization is finished use a while(true) to stay in that loop. – Michel Keijzers Apr 9 '18 at 13:06
  • @EdgarBonet Btw, I never tried an ISR during initialization myself. – Michel Keijzers Apr 9 '18 at 13:06
  • 1
    @EdgarBonet having an ISR "return to the caller" of a blocked function is approximately what a scheduler does. What would be more realistic here is to have the ISR set a "timed out" flag and have whatever operation is spinning in a while() give up on seeing that. Checking elapsed time via millis() is comparable, only instead of a boolean flag it's a numeric value updated by the Arduino core's timer ISR handler. – Chris Stratton Apr 9 '18 at 13:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.