The Arduino while loop, on a reaction timer
Intermediate
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
while (condition) { ... } runs its block again and again for as long as the condition is true. It checks before every pass, so if the condition is false to begin with, the block never runs at all. Its most common job on an Arduino is waiting: while (digitalRead(2) == HIGH) { } does nothing, over and over, until the button on pin 2 is pressed.
The circuit above is a reaction timer. Press Run and wait. The green lamp lights at a random moment, and the serial monitor prints how many milliseconds you took to press the button. In our run the lamp lit 1.94 seconds after Run, and a press a quarter of a second later printed "reaction: 251 ms". Press before the lamp and it prints "too soon" and the buzzer growls.
The whole game is four while loops. Below is how they work, how while differs from for and do while, and the three ways a while loop goes wrong, each run on an Uno with what it printed.
Four while loops make a game
Here is the heart of the sketch running above. The first loop is the random wait, and it keeps going while the time since started is less than wait. Inside it, a check for the button catches a false start, and a second, smaller loop, while (pressed()) delay(5);, waits for you to let go before the round starts again.
Once the wait is over the lamp lights, and while (!pressed()) delay(1); is the third loop: it does nothing but wait, a millisecond at a time, until the button goes down. The time it waited is your reaction. The fourth loop, at the end of the round, waits for the button to come back up so that one long press is not counted twice.
unsigned long wait = (unsigned long)random(1500, 4000);
unsigned long started = millis();
while (millis() - started < wait) {
if (pressed()) {
Serial.println("too soon");
growl();
while (pressed()) delay(5);
delay(500);
return;
}
delay(5);
}
digitalWrite(LAMP, HIGH);
unsigned long lit = millis();
while (!pressed()) delay(1);
unsigned long took = millis() - lit;while, for and do while
Use for when you know how many times around you want to go, and while when you only know when to stop. Doubling a number until it passes 100 is a while job, because nobody wants to work out the count first. The sketch below printed 128.
do { ... } while (condition); is the same loop turned upside down: the block runs first and the test comes after, so it always runs at least once. Below, x is 10, so while (x < 5) never ran its block and the do version ran it once and printed "do ran". Note the semicolon after a do while; it is the one loop that needs one.
void setup() {
Serial.begin(9600);
int n = 1;
while (n < 100) {
n = n * 2;
}
Serial.println(n);
}
void loop() {
}Wait for a button
The empty loop below is the simplest useful while there is. With the button wired from pin 2 to ground and the pull-up switched on, the pin reads HIGH until it is pressed, so the loop spins with nothing to do until then. We pressed 1.2 seconds in and it printed "pressed at 1199 ms".
The braces can be empty, but they cannot be missing: while (digitalRead(BUTTON) == HIGH); with a semicolon does the same thing, and it is so easy to misread that most people write { } on purpose.
const int BUTTON = 2;
void setup() {
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
Serial.println("press the button");
while (digitalRead(BUTTON) == HIGH) {
}
Serial.print("pressed at ");
Serial.print(millis());
Serial.println(" ms");
}
void loop() {
}Loops that never end
A while loop stops only when something inside it makes the condition false. Forget to change the counter and it never does. The sketch below never adds one to i, so it printed 0 six times in just over half a second and would have kept going for as long as the board had power.
A semicolon after the condition does the same thing more quietly. In while (i < 3); {, the semicolon is the whole loop and the block after it is outside, so i can never change. That sketch printed nothing at all, not even the "done" after the loop, and the build gave no warning. If your sketch stops dead after a while, look at the end of that line first.
Sometimes you want a loop that never ends. while (true) { } at the end of setup() stops a sketch on purpose: ours printed "setup" and never once printed the "loop" that loop() would have.
void setup() {
Serial.begin(9600);
int i = 0;
while (i < 3) {
Serial.println(i);
delay(100);
}
}
void loop() {
}break out, and how fast a loop goes around
break leaves a loop on the spot, which makes while (true) with a break inside a handy shape when the reason to stop is worked out halfway through. Below, the loop adds 1, 2, 3 and so on until the total passes 50, and printed "10 55": ten numbers, totaling 55.
An empty while loop is very fast. Counting its own passes for one second of the board’s clock, ours went around 353287 times. Put one analogRead() inside and it managed 9139, because each reading takes about a tenth of a millisecond. That is why a sketch that waits in a while loop is still quick to notice a press.
void setup() {
Serial.begin(9600);
int total = 0;
int n = 0;
while (true) {
n++;
total = total + n;
if (total > 50) break;
}
Serial.print(n);
Serial.print(" ");
Serial.println(total);
}
void loop() {
}The loop that freezes everything
While a while loop is waiting, nothing else in the sketch happens. In the sketch below, an LED blinks and prints the time, and a while loop waits whenever the button is held. It printed "blink at 499" and "blink at 999", and then we held the button from 0.6 to 1.8 seconds. The next line was "blink at 2299": the blink simply stopped while the loop waited.
For the reaction timer that is exactly right, because there is nothing else to do. For a sketch that must keep blinking, reading a sensor or listening to the serial monitor, do not wait in a loop. Let loop() go around, check the button with an if each time, and time things with millis(), as the blink without delay page shows.
const int BUTTON = 2;
void setup() {
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(250);
digitalWrite(13, LOW);
delay(250);
Serial.print("blink at ");
Serial.println(millis());
while (digitalRead(BUTTON) == LOW) {
}
}Questions
What is the difference between a while loop and a for loop?
A for loop is for a known number of passes and keeps its counter in its own brackets. A while loop only has a condition and runs until it turns false, which suits waiting for a button, a sensor or serial input.
What is the difference between loop() and while?
loop() is the function the Arduino calls over and over after setup(). while is a statement inside your code. A while loop inside loop() holds up loop() until it finishes.
Why is my while loop stuck?
Either nothing inside it ever makes the condition false, or there is a semicolon straight after the condition, as in while (i < 3); which loops on nothing forever. Print something inside the loop to see whether it is going around.
How do I exit a while loop in Arduino?
Make the condition false from inside the loop, or use break, which leaves the loop at once. return leaves the whole function the loop is in.
How do I wait for a button press in Arduino?
With the button from the pin to ground and pinMode(pin, INPUT_PULLUP), while (digitalRead(pin) == HIGH) { } waits until it is pressed. Nothing else in the sketch runs while it waits.
Keep going
Where the facts on this page come from
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.