2
\$\begingroup\$

My code:

#define MAX 10
const int LED1 = 2;
const int LED2 = 8;
const int LED3 = 4;
const int LED4 = 5;
int val;
int array[MAX];
int counter = 0;
int i;
int old_b = 0;
int error;

void setup() { 
 Serial.begin(9600);
 pinMode(A5, INPUT_PULLUP); 
}

int readButtons(int pin) {
 int b,c;
 c=analogRead(pin);
 Serial.print("analogRead = ");
 Serial.println(c); 
 delay(100);
 if (c>1015) b = 0; 
 else if (c>70 && c<76) b = 1; 
 else if (c<128 && c>122) b = 2; 
 else if (c>169 && c<175) b = 3; 
 else if (c>209 && c<217) b = 4;
 else if (c>247 && c<256) b = 5;
 else if (c>280 && c<291) b = 6;
 else b = 0;
 if (b == old_b) {
  return 0;
  old_b = b;
 } else {
   return b;
   stari_b = b;
 }
}

void loop() {
 while ((val = readButtons(5)) != 5) {
  if (val == 1) {
   array[counter] = 1;
   counter++;
   Serial.print("In ");
   Serial.print(counter);
   Serial.print(" saving ");
   Serial.println("1");
   delay(500);
  } else if (val == 2) {
     array[counter] = 2;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("2");
     delay(500);
  } else if (val == 3) {
     array[counter] = 3;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("3");
     delay(500);
  } else if (val == 4) {
     array[counter] = 4;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("4");
     delay(500);
  }
  if (counter == MAX) {
   counter = 0;
  }
 }

 for (i = 0; i < MAX; i++) {
  if (array[i] == 1) {
     digitalWrite(LED3, HIGH);
     digitalWrite(LED4, HIGH);
     delay(1000);
     digitalWrite(LED3, LOW);
     digitalWrite(LED4, LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 2) {
     digitalWrite(LED1, HIGH);
     digitalWrite (LED2, HIGH);
     delay(1000);
     digitalWrite(LED1, LOW);
     digitalWrite(LED2,LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 3) {
     digitalWrite(LED2, HIGH);
     digitalWrite(LED4, HIGH);
     delay(1000);
     digitalWrite(LED2, LOW);
     digitalWrite(LED4, LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 4) {
     digitalWrite(LED1, HIGH);
     digitalWrite(LED3, HIGH);
     delay(1000);
     digitalWrite(LED1, LOW);
     digitalWrite(LED3, LOW);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }

 } 
}

So I have buttons connected like this, except I have them 5. When one of the first four is pressed sketch stores 1,2,3 or 4 to denote which buttons was pressed and stores it in integer array. When the fifth (the "GO" button) button is pressed while loop ends and starts the for loop which according to the buttons pressed blinks LEDs. I want to make that while Arduino is blinking LEDs , that is while for loop is being executed, if at any time the fifth "GO" buttons is pressed it should stop blinking LEDs and start program from the beginning ( void loop { ... } ) that is again wait for the presses of first four buttons.


I managed to make this:

#define MAX 50
const int LED1 = 2;
const int LED2 = 3;
const int LED3 = 4;
const int LED4 = 5;
int array[MAX];
int old_b = 0;
int val;
int counter = 0;
int i;
int temp;
int L1;
int L2;

void setup () {
  pinMode (A5, INPUT_PULLUP);
  Serial.begin(9600);
}

int readButtons (int pin) {
  int b, c;
  c = analogRead(pin);
  Serial.print("analogRead =  ");
  Serial.println(c);
  delay(100);
  if (c > 1015) b = 0;
  else if (c > 70 && c < 76) b = 1;
  else if (c > 122 && c < 128) b = 2;
  else if (c > 169 && c < 175) b = 3;
  else if (c > 209 && c < 217) b = 4;
  else if (c > 247 && c < 256) b = 5;
  else if (c > 280 && c < 291) b = 6;
  else b = 0;
  if (b == old_b) {
   return 0;
   old_b = b;
  } else {
    return b;
    old_b = b;              
    }                           
}

void loop () {
  while ((val = readButtons(5)) != 5) {
    if ((val == 1) || (val == 2) || (val == 3) || (val == 4)) {
      array[counter] = val;
      Serial.print("In  ");
      Serial.print(counter);                
      Serial.print(" saving ");            
      Serial.println(val);
      delay(200);
      counter++;
      if (counter == MAX) {
        counter = 0;
      } 
    }
  }

  temp = counter;
  counter = 0;

  for (i = 0; i < temp; i++) {
    if (array[i] % 2 == 0) {
      L1 = 2;
      L2 = array[i] / 3 + 3; 
    } else {
      L2 = 5;
      L1 = array[i] % 3 + 3;    
      }

    if (readButtons(5) != 5) {
     digitalWrite (L1, HIGH);
     if (readButtons(5) != 5) {
      digitalWrite (L2, HIGH);
      delay(1000);
      digitalWrite (L1, LOW);
      digitalWrite (L2, LOW);
      if (readButtons(5) == 5) {
        i = temp;
      }
     } else {
       digitalWrite (L1, LOW);
       i = temp; 
     }
    }

  }
}

So I still have a function which detects if a button was pressed. Prior to that, I have determined in which range are values for certain button. In the while () {...} loop, while the 5th button isn't pressed, Arduino is "looking" if any of first four buttons was pressed. If it was store it in an array and if not keep "looking". When 5th button is pressed, we brake out and store last known place in array in which value was stored and set the place counter to zero so when starting again, Arduino stores values in the first place of array. Then based on that what is stored I determine which pin/LED will light up. If at any time during blinking LEDs 5th button is pressed again, Arduino stops the blinking and again waits for the presses from first four buttons. That is how it is supposed to work in theory. In practice I still can't get it to stop blinking every time 5th button is pressed again. I have to press it multiple times, sometimes two or sometimes even more times. I don't think using interrupts will help, since I don't know how I could use them in my problem.

\$\endgroup\$
12
  • 2
    \$\begingroup\$ Welcome to EE.SE! Your question is about C programming and is not specific to any microcontroller, much less the arduino. I've suggested that it be moved to another site, on more suited to software questions. \$\endgroup\$
    – Joe Hass
    Jan 9, 2014 at 22:17
  • \$\begingroup\$ @Joe I disagree. The question is about how to create a situation which will restart execution of the main method after a button press or other external input. For example, how to utilize a hardware interrupt for this purpose. Also, the OP stated that it is for Arduino, which is based on the Atmel AVR microcontroller. \$\endgroup\$
    – JYelton
    Jan 9, 2014 at 22:26
  • \$\begingroup\$ Though I'm not familiar with an Arduino, what needs to happen is: 1. Write an interrupt to handle the button press; 2. Make the interrupt return, not to where you were last but to the beginning of loop. How you do this depends on your microcontroller and the way in which it handles interrupts (stacks/unstacks). \$\endgroup\$ Jan 9, 2014 at 22:36
  • \$\begingroup\$ This question appears to be off-topic because it is about straight programming. \$\endgroup\$
    – Matt Young
    Jan 9, 2014 at 23:00
  • \$\begingroup\$ Using an interrupt to break out of a loop is enormously dangerous. If you don't back out of all the nested loops in an orderly fashion the stack will have dead storage (at best) or be corrupted. What the OP wants is a straight programming question in the C language and is independent of processor. \$\endgroup\$
    – Joe Hass
    Jan 9, 2014 at 23:05

3 Answers 3

3
\$\begingroup\$

Are you sure you need to exit at ANY time wherever the execution pointer is? Normally, you could just check the button press at the end of the while and for loops and call return when it is pressed.

Sometimes we think our program has to do something and break out of execution, but upon careful examination, we conclude that it can finish one loop interation and then quit.

Could you tell us what's done inside the loops, so we can make sure what's needed?

Edit: As I suspected, the problem with your program is that your delay intervals are too long, e.g. delay(1000) and delay(500), and you can't check for button presses while the program is waiting.

To fix that, create the following function, that will replace your calls to delay:

// waits for the specified miliseconds 
// or return (almost) immediately if button is pressed
// return value indicates whether button was pressed or not
boolean waitOrButtonPressed(int miliseconds) {
  for (int i = 0; i < miliseconds / 10; i++) {
    if (readButtons(5)) == 5)
      return true;
    delay(10);
  }
  return false;
}

Then, inside your for loop, replace your calls to delay(1000) by the following code:

if (waitOrButtonPressed(1000)) return;

The function I created will make your program wait almost like delay(), but will return true if a button was pressed during that time. If that happens, the if statement above will exit loop() which will be called again by the Arduino generated code.

Remember to keep the miliseconds to wait when you replace your calls to delay() to keep your program waiting behavior unchanged.

Edit 2: Ramblings

Cases like this remind me of the movie Tron, in which the main character is dragged into a computer, where time passes much slowlier than in real life. So, like in the movie, 1000 miliseconds (1s) is a LOT OF TIME for the MCU. You can do a whole lot more than waiting in that time. Try and break up the long Waits and do useful stuff with your MCU cycles.

Also, learn how to use interrupts, like others mentioned in their comments and answers. It's just another (a bit more complicated) way of doing stuff while you wait. In your case, interrupts could be use to detect the button press without the need to check it everytime everywhere. Here's a place to start learning about interrupts with Arduino.

\$\endgroup\$
9
  • \$\begingroup\$ I have tried using your function but I couldn't get it to work. I also have checked interrupts. In every example I have found when it is triggered some function is called, but I would need it when triggered (only during executing for loop) to start the whole program from the beginning that is to start executing void loop {...}. Is that possible? \$\endgroup\$
    – Nick
    Jan 10, 2014 at 10:46
  • \$\begingroup\$ @Mate Why it didn't work? What happens? Did it compile ok? Or it is now doing what you intended it to? As for the interrupt, you could detect the button press in the interrupt (o pin level change one) and set a flag there, but you would still have to check whether the flag was set in the loop, much like we are trying to do with the function I gave you. As others did, I wouldn't advise you to try to force exit of loop() function directly from the interrupt. That would require you to make verly low level stuff that is difficult to make, debug and mantain, and that you really don't have to do. \$\endgroup\$
    – Ricardo
    Jan 10, 2014 at 10:56
  • \$\begingroup\$ In my code, I didn't know exactly how to detect your button press, so I just added what I would normally do in my code. So, you have to change the part if (digitalRead(fifthButton) == HIGH) to whatever you do to check if your button was pressed. I've noticed that you are using a analog pin for detecting if that button is pressed, so this might be what needs to be fixed. \$\endgroup\$
    – Ricardo
    Jan 10, 2014 at 10:58
  • \$\begingroup\$ Ok, now I see that you are using a voltage divider on an analog pin to read several buttons using a single pin. Great, I do that all the time, too. So I changed the way I check whether the 5th button is pressed in my answer. Check if it works now. \$\endgroup\$
    – Ricardo
    Jan 10, 2014 at 11:05
  • \$\begingroup\$ When I press 5th button for the start of blinking and for the end of blinking LEDs Arduino doesn't recognize it immediately, that is I have to press several times to start blinking and several times to stop blinking. How many times I have to press to start/end blinking is different every time. \$\endgroup\$
    – Nick
    Jan 10, 2014 at 12:02
2
\$\begingroup\$

One method that can be used and has the effect of reseting the mcu (I'm not sure if this is the effect you are after) is to use an external interrupt for the button and inside the interrupt enable the watchdog with a small timeout and let it expire and reset the mcu.

\$\endgroup\$
3
  • \$\begingroup\$ +1 This is about the only safe way to use a button press to break out of the main loop in an embedded program. If the OP can live with a hard processor reset then this would be the way to do it. \$\endgroup\$
    – Joe Hass
    Jan 9, 2014 at 23:07
  • \$\begingroup\$ If it is OK I can change the question and write the actual code which I'm using. I gave a bit more general question so it could be used for more things then for what I'm trying to acomplish. \$\endgroup\$
    – Nick
    Jan 9, 2014 at 23:10
  • \$\begingroup\$ @Mate If you think that this doesn't reply your question and that the detailed code will help you get a more suited solution then I guess you can. I hope it doesn't change the essence of the question. \$\endgroup\$
    – alexan_e
    Jan 9, 2014 at 23:29
0
\$\begingroup\$

This is why C has the goto statement; for moving forward out of many nested loops.

Most people will tell you not to use it, and it's likely you can work around it, but it's certainly a solution to your problem.

\$\endgroup\$
7
  • \$\begingroup\$ Why the down vote? \$\endgroup\$
    – Samuel
    Jan 15, 2014 at 5:32
  • 2
    \$\begingroup\$ MOST times (though not all)goto is used, its pretty bad coding style. That's why most people tell you not to use it. There's usually better ways to handle it. \$\endgroup\$ Jan 15, 2014 at 11:45
  • \$\begingroup\$ ... and the downvote didn't come from me \$\endgroup\$ Jan 15, 2014 at 11:46
  • \$\begingroup\$ You've pretty much summarized the second sentence in my answer. So, clearly I agree. Still, it is a valid solution. \$\endgroup\$
    – Samuel
    Jan 15, 2014 at 17:56
  • 1
    \$\begingroup\$ Whoever made the downvote, it's legitimate. Because goto should not even be considered to be mentioned. Saying, "it's pretty bad coding style", is still an understatement. goto is a relict of (programming like) Assembler where it's a natural part of the vocabulary. Splitting up lengthy code into functions, calling those when needed and using return, break or continue makes the use of goto obsolete. Also, deep nesting creates algorithmic complexity, which should be avoided. Bottom line, there is no need for goto if you know programming. Seriously. \$\endgroup\$
    – Peterino
    Feb 24, 2018 at 21:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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