Timing with millis(): keep your sketch responsive
Schedule small tasks with elapsed time instead of long blocking delays.
Goal: blink the built-in UNO LED while leaving loop() free to read inputs. You only need the UNO and a data-capable USB cable. A long delay() pauses normal sketch execution, so a short button press during that pause can be missed.
millis() reports elapsed milliseconds since startup. Store timestamps in an unsigned 32-bit type and compare elapsed time by subtraction. The pattern uint32_t now = millis(); if ((uint32_t)(now - last) >= interval) { last = now; /* do one short task */ } works across the 32-bit counter rollover for suitably short intervals. Avoid comparing now to last + interval, which can fail at rollover.
Try this complete UNO sketch: unsigned long last = 0; bool on = false; void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); } void loop() { unsigned long now = millis(); if ((unsigned long)(now - last) >= 500) { last = now; on = !on; digitalWrite(LED_BUILTIN, on ? HIGH : LOW); } }. Copy the sketch into a new Arduino IDE sketch. No additional library is required.
The LED changes state every 500ms. Add a sensor read outside the timing block: it can run whenever loop() returns, rather than waiting for a half-second delay. Give each independently scheduled task its own last-run timestamp.
Challenge: look at the Breathing LED and Touch-Controlled LED sketches. One uses a periodic update, the other waits for the input to remain stable. Both use elapsed time, but they solve different problems.
This pattern is cooperative scheduling, not automatic parallel execution. A slow sensor call, network request or long motor movement can still block the loop. Keep tasks short, add timeouts where supported and check that each function returns promptly.
References: https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay/ and https://docs.arduino.cc/language-reference/en/functions/time/millis/