Interrupt Cancel Button

I'm making an Arduino Application that has multiple separate programs. There are seven LEDs that light up when their respective program is running. There are also three tactile switches that make a kind of interface for navigating between programs. When you press the left button, the Arduino executes the previous program, etc.

My problem is that when each program is running, most of the time, the Arduino is just standing by, waiting for the delay(); to finish. When that's happening, I want it to be listening for a 'Cancel Program Button.' When that's pressed, the user can navigate between programs again. How would I write some code to stop a delay(); and leave the Arduino open for more processing?

You can write your own implementation of the delay() function that uses the one in the wiring.c part of the code as a starting point:

void delay(unsigned long ms)
{
      unsigned long start = millis();
      
      while (millis() - start <= ms) {
            // Your code starts here
               if (buttonPressed()) return;
               // Your code ends
        }
}

One way would be to use an interrupt.

You could be to put it in the serial-interrupt... but you don't have access to that interrupt when using the Arduino serial library. The Arduino serial-interrupt code just takes the byte from the computer and adds it to a buffer.

Anyhoo, you'd have to abandon the Arduino serial library to do this.

gabebear, I am not sure what you have in mind but I think that RuggedCircuits approach is more appropriate for this application than using an interrupt. Here is another variation on his suggestion that returns true if a switch is pressed while waiting and shows another way to wait for a variable number of milliseconds.

boolean myDelay(unsigned long ms)
{
//return true when button pressed, otherwise return after given delay period

   while(ms > 0)
   {
     if (buttonPressed()) 
        return true;      
     --ms;
     delay(1);
   }
   return false;   
}

Wow! Thanks! I was looking into doing it like this: http://gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

But that looks much more efficient. I'm gonna try it now...

The code in that link must have been written before attachInterrupt was added to the Arduino core. If that code was written today, attachInterrupt would be a more simple and portable way to implement it.

Anyway, interrupts don’t help in your application because they do not make it any easier for you to exit execution of a function.

Ya, it probably would be best to not be break too much with the Arduino library, but there are advantages to directly making interrupts. If you use attachInterrupt(), you sacrifice some speed, but not much(3 or so cycles).

SIGNAL(SIG_INTERRUPT1) {
  if(intFunc[EXTERNAL_INT_1])
    intFunc[EXTERNAL_INT_1]();
}

Here is a sample program that uses the serial interrupt to implement a menu system.

This works with Arduino 16, it may fail in earlier versions.

*** UPDATE *** this code has serious flaws!!!

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void serialOpen2(long baud);
void serialWrite2(unsigned char c);
void serialPrint2(const char *s);

void setup() {
        serialOpen2(9600);
        pinMode(13, OUTPUT);
        pickProgram();
}

void loop() { }


void pickProgram() {
        digitalWrite(13, LOW); // make sure the led is off
        serialPrint2("Pick a program:\n");
        serialPrint2("  'n' : do NOTHING!!\n");
        serialPrint2("  'b' : BLINK!!\n");
        serialPrint2("\n");
        serialPrint2(" Press 'm' to return to this menu\n\n");
        
        while(1); // just wait here forever
}

// endlessly print that you aren't doing anything
void nothing() {
        while(1) {
                digitalWrite(13, LOW); // make sure the led is off
                serialPrint2("I'm not doing anything...\n");
                delay(1000);
                serialPrint2("still not doing anything...\n");
                delay(1000);
        }
}

// endlessly blink LED 13
void blink() {
        while(1) {
                serialPrint2("AHH!!! it's DARK\n");
                digitalWrite(13, HIGH);
                delay(1000);
                serialPrint2("The light is BLINDING!!!\n");
                digitalWrite(13, LOW);
                delay(1000);
        }
}


// interrupt for serial recieve
//  this is for getting input for the menu
ISR(USART_RX_vect) {
        
        
        // do whatever for the menu-item
        switch(UDR0) {
                case 'm':
                case 'M':
                        sei(); // re-enable interrupts
                        serialPrint2("\n\n\n");
                        pickProgram();
                        break;
                case 'n':
                case 'N':
                        sei(); // re-enable interrupts
                        serialPrint2("\n\n\n");
                        nothing();
                        break;
                case 'b':
                case 'B':
                        sei(); // re-enable interrupts
                        serialPrint2("\n\n\n");
                        blink();
                        break;
        }
}

// open the serial port
void serialOpen2(long baud) {
      UBRR0H = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8;
      UBRR0L = ((F_CPU / 16 + baud / 2) / baud - 1);
        
      // enable the serial pins
      sbi(UCSR0B, RXEN0);
      sbi(UCSR0B, TXEN0);
      
      // enable the serial interrupt
      sbi(UCSR0B, RXCIE0);
}

// write a byte to the serial port
void serialWrite2(unsigned char c) {
      while (!(UCSR0A & (1 << UDRE0)));
      UDR0 = c;
}

// print a string to the serial port
void serialPrint2(const char *s) {
      while (*s) serialWrite2(*s++);
}

Remember that if you call ANY of Arduino’s built-in serial functions, you will get a linker error.

I think this is pretty much exactly what was asked for… but it does break with the Arduino library.

I was making something similar to this for my own project…

I think this is pretty much exactly what was asked for...

Hi gabebear, the OP wants to navigate using three tactile switches. How does the code you posted do anything like that.

Ooops, yep, well then using the external interrupt is pretty easy.

I saw something like what I was doing and... well... the world is a nail.

Haha. Aw well. Thanks for the code though. I think this is possible! Yay! Thanks for all of the help, people. It's really helpful.

EDIT: Whoa. That was a little redundant. It is really helpful, though.

gabebear:

How much did you test your code?

It looks to me like it suffers from a serious case of infinite recursion.

  • Brian

@Coding Badly : I tested the program, it should run on any arduino you have laying around.

The beauty of interrupts is you can have infinite recursion in your main program, the program I posted is just a bunch of infinite loops.

I interrupt the current infinite loop whenever I get a byte on the serial interface and pick a different infinite loop.

Hmm... I guess there might be a problem with my infinite recursion. I doubt the code I posted suffers from it, but it could easily.

I might overrun the stack, if my calls to serialWrite2() or serialPrint2() put anything on the stack, they could overflow the stack after many thousand menu presses. One fix would be to force the stack pointer back to it's original position every time you select from the menu...

All C calls push values on the stack. You will run out of RAM.

The best fix is to forget about interrupts and use a state machine to drive your menu ;)

All C calls push values on the stack.

C function calls will push onto the stack, unless they get inlined, which it looks like everything does if you look at the hex file in this case… but I was just lucky. This is a bad idea.

I’ve been using some fully static/reentrant languages (ancient SPEL) lately and wasn’t thinking C-ish. All variables in Spel are static and normal functions don’t return… which is a blessing and a curse.