Because they block. Same applies for for-loops. There are a few exceptions; e.g. setting elements of an array or reading an array.
Assume that you want to blink a LED and want to detect a button press using the simple blink example. Add a button between pin and GND.
const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(115200);
}
void loop()
{
digitalWrite(ledPin, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(ledPin, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
// wait till button is pressed
while (digitalRead(btnPin) == HIGH) {}
// continue the code
Serial.println(F("Button was pressed"));
}
The effect is that you will only see one blink and next the code waits for the button to be pressed. The Serial.print is just to demonstrate some action that you want to take.
Now replace the while with an if. If the button is not pressed, the code will keep blinking the LED.
const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(115200);
}
void loop()
{
digitalWrite(ledPin, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(ledPin, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
// if button is pressed
if (digitalRead(btnPin) == LOW)
{
Serial.println(F("Button is pressed"));
}
// continue the code
}
Now this is still not perfect because the button will only be read every 2 seconds. To solve that, you have to use a none-blocking blink as demonstrated in blink-without-delay. Below is a way to achieve it
const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(115200);
}
void loop()
{
// remember the state of the LED
static uint8_t ledState = LOW;
// remember the last time that the LED was updated
static uint32_t delayStarttime;
// if it's time to update the LED
if (millis() - delayStarttime >= 1000)
{
// remember the time that we did update the LED
delayStarttime = millis();
// toggle the LED's state
ledState = !ledState;
// update the LED
digitalWrite(ledPin, ledState);
}
// if button is pressed
if (digitalRead(btnPin) == LOW)
{
Serial.println(F("Button was pressed"));
}
// continue the code
}
And in the next step you can place the blink functionality in a function
const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(115200);
}
// the loop function runs over and over again forever
void loop()
{
// blink the LED
blink();
// wait till button is pressed
if (digitalRead(btnPin) == LOW)
{
Serial.println(F("Button was pressed"));
}
// continue the code
}
/*
non-blocking blink
*/
void blink()
{
// remember the state of the LED
static uint8_t ledState = LOW;
// remember the last time that the LED was updated
static uint32_t delayStarttime;
// if it's time to update the LED
if (millis() - delayStarttime >= 1000)
{
// remember the time that we did update the LED
delayStarttime = millis();
// toggle the LED's state
ledState = !ledState;
// update the LED
digitalWrite(ledPin, ledState);
}
}
Only thing to optimise in the above code is the magic number 1000.
I prefer to use static variables. They are remembered between the different calls of the function (like global variables are) but are local to the function (like normal variables); the advantage of the latter is that you can use the same variable name in different functions without them interfering with each other.
I hope this clarifies it a bit.