A practical question which comes up every day is how to blink two LEDs at different rates? That simply means an independent control on each LED. That small wish is a big jump to visual animation. In real life, the multi-coloured Christmas lights (aka Dewali lights) blinks in that fashion to give us a great feel. Problem to most of the beginners is with the usual “blink” tutorial program :
1 2 3 4 5 6 7 8 9 10 11 12 | void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); // set the LED on delay(1000); // wait for a second digitalWrite(13, LOW); // set the LED off delay(1000); // wait for a second } |
This program works fine to blink one LED. But to blink two LEDs, unless you want to blink them both at once or one after the other, you lack control. If you want to blink the two LEDs at different rates such as once a second for LED 1 and twice a second for LED 2 then the delay()
function is not what you are looking for. That is, you can’t do anything else until the one task at hand is over. That is called “blocking”. To resolve this problem of “blocking”, there are many solutions, more or less difficult:
- using millis()
- using a table-driven approach
- using a Finite-State Machine logic
By using a clever trick with milis()
, we will not need to call delay()
for one LED:
---
1 2 3 4 5 6 7 | void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, (millis() / 1000) % 2); } |
Arduino’s millis()
function returns the number of milliseconds the program has started running. We divide this value by 1000 and get the number of seconds passed. Then we take the number of seconds and divide it by two using the modulus (%) operator. This calculation returns 0 for even numbers and 1 for odd numbers. This approach is no longer blocking – we can perform another task within the loop.
Dan Hoover’s article “Understanding Blink Without Delay” has an example with 3 LEDs using millis()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | int red = 2; int intervalRed = 1000; //how long to delay in millis unsigned long previousRed = 0; int redState = LOW; int blue = 3; int intervalBlue = 2500; //how long to delay in millis unsigned long previousBlue = 0; int blueState = LOW; int green = 4; int intervalGreen = 5000; //how long to delay in millis unsigned long previousGreen = 0; int greenState = LOW; void setup() { Serial.begin(9600); pinMode(red, OUTPUT); pinMode(blue, OUTPUT); pinMode(green, OUTPUT); } void checkRed() { unsigned long currentMillis = millis(); if (currentMillis - previousRed >= intervalRed) { //save this reading! previousRed = currentMillis; //figure out if you should turn the LED on or off if (redState == LOW) { redState = HIGH; } else { redState = LOW; } digitalWrite(red, redState); } } void checkGreen() { unsigned long currentMillis = millis(); if (currentMillis - previousGreen >= intervalGreen) { //save this reading! previousGreen = currentMillis; //figure out if you should turn the LED on or off if (greenState == LOW) { greenState = HIGH; } else { greenState = LOW; } digitalWrite(green, greenState); } } void checkBlue() { unsigned long currentMillis = millis(); if (currentMillis - previousBlue >= intervalBlue) { //save this reading! previousBlue = currentMillis; //figure out if you should turn the LED on or off if (blueState == LOW) { blueState = HIGH; } else { blueState = LOW; } digitalWrite(blue, blueState); } } void loop() { checkRed(); checkGreen(); checkBlue(); } |
I provided the above example simply because on this webpage you’ll get the emulation against the above code (click “Run the code” button and watch the 3 LEDs above the video). Using millis()
is not super-easy but we use milis()
in lot of sketches for Arduino and ESP32.