Search code examples
cembeddedarduinomicrocontrollerinterrupt

Arduino interrupt alternatives


From what I've read, the solution to my problem is to use an interrupt, but if I understand them correctly, I can't use a delay in the routine that gets called by the interrupt. I've got a large pushbutton LED switch. I want it to have a heartbeat while sitting idle, but once it's pushed, stay green and execute code.

I can break the heartbeat() if I push the button enough times (I assume getting the state change at just the right time as it finishes a loop of the heartbeat), but I'm stuck on how to make it work on the first click. Is there an alternative way to do what I'm attempting?

void loop(){

heartbeat();                                    //Make LED beat.
buttonVal = digitalRead(buttonPin);             //Check the button.
    if (buttonVal != buttonState) {               //If the button state changed.
        if (buttonVal == HIGH){                   //Check if the button is pressed.
            analogWrite(greenPin, 255);         //Button stays green once pushed.
            functionA                           //Has some delays in it.
            functionB                           //Has some other delays.
        }
    }
}

void heartbeat(){
    for(i = 0; i < pmw; i++) {
        analogWrite(greenPin,i);
        delay(((60000/rate)*.1)/pmw);
    }

    for (i = pmw; i > 0; i--){
        analogWrite(greenPin,i);
        delay(((60000/rate)*.2)/pmw);
    }

    for(i = 0; i < pmw; i++) {
        analogWrite(greenPin,i);
        delay(((60000/rate)*.1)/pmw);
    }

    for (i = pmw; i > 0; i--){
        analogWrite(greenPin,i);
        delay(((60000/rate)*.6)/pmw);
    }
 }

Solution

  • You are correct in most of your assumptions. The proper way to handle this is using an interrupt and it is not a good idea to have delays in your interrupt service routines (ISR). So what you want to do is set a flag in your ISR and check that flag in your main loop.

    // Flag needs to be volatile if used in an ISR
    volatile int buttonFlag = 0; 
    
    void loop()
    {
        if (buttonFlag == 0)
        {
            heartbeat();                        //make led beat
        }
        else
        {
            analogWrite(greenPin, 255);         //button stays green once pushed
            functionA                           //has some delays in it
            functionB                           //has some other delays
            buttonFlag = 0;                     //clear flag after executing code
        }
    
    }
    
    // Interrupt Service Routine attached to INT0 vector
    ISR(EXT_INT0_vect)
    {
        buttonFlag = digitalRead(buttonPin);    //set flag to value of button
    }
    

    Since the interrupt will only trigger on a change in the state of the button, you don't need to check for that.

    Make sure that your flag variable is global and is declared volatile for use in the ISR. And make sure you are using the correct interrupt vector for use with the pin you are using.

    Here is a good tutorial on Arduino interrupts. And here is another good example of what you're trying to do.

    You may also want to look into debouncing your switch presses depending on what type of switch you're using. If instead of missing the first press, you're getting too many presses, you will need to implement some type of debouncing.