blinking LED without delay()

Andy’s step sequencer was coming along nicely until he added a delay to blink his LED to visualise the tempo.  The delay would slow down the whole main loop, meaning that all other readings (like push buttons) had to wait for the end of the delay before they were activated.

Here’s Andy’s lovely breadboard:

It just can’t be possible that Arduino has such a fundamental flaw, can it?  Well, kinda, but it turns out the problem can be solved.  In fact, the solution was right under our noses…

Here’s the code:

// select the input pin for the potentiometer
int potPin = A0;
// variable to store the value coming from the pot 
int tempoPotVal;
int potVal = 0;
// on-board LED shows us the delay of the main loop
int loopLED = 13;

// LED shows us the tempo set by the pot
int tempoLED = 12;

// LED indicates the button toggle state
int buttonLED = 7;

// pushbutton to toggle the LED
int buttonPin = 8;
int buttonState;
int prevButtonState = 1;

// initial state and saved last state for button LED
int buttonLEDState = HIGH;
int prevButtonLEDState;

// initial state and saved last state for tempo LED
int tempoLEDState = LOW;
// will store last time LED was updated
long previousMillis = 0;

void setup() {
 // declare the pins as either INPUT or OUTPUT:
 pinMode(loopLED, OUTPUT);
 pinMode(tempoLED, OUTPUT);
 pinMode(buttonLED, OUTPUT);
 pinMode(buttonPin, INPUT);

 // set arduino's internal resistor to pull down
 digitalWrite(buttonPin, LOW);
}

void loop() {
 // this bit here just shows us how quickly the main loop is looping
 // it will run constantly and the delay is what makes it blink
 digitalWrite(loopLED, HIGH);
 delay(100);
 digitalWrite(loopLED, LOW);
 delay(100);

 // read the value from the button
 // (every time we go through the loop)
 buttonState = digitalRead(buttonPin);

 // button is pressed
 if ((buttonState == 0) && (buttonState != prevButtonState)) {
  if (buttonLEDState == HIGH) {
   buttonLEDState = LOW;
   digitalWrite(buttonLED, buttonLEDState);
  }
  else {
   buttonLEDState = HIGH;
   digitalWrite(buttonLED, buttonLEDState);
  }
  prevButtonLEDState = buttonLEDState;
 }
 // set a new previous button state
 prevButtonState = buttonState;

 // read the value from the potentiometer
 // (every time we go through the loop)
 potVal = analogRead(potPin);
 // map the min and max vals from the pot to the values we want
 //to blink the LED
 // my potentiometer gave values between 0 and 671 or so, 
 // yours may be different
 tempoPotVal = map(potVal, 680, 0, 10, 1000);

 // get the current time since arduino started running the program
 unsigned long currentMillis = millis();

 // is the difference now and the last time we blinked the LED bigger
 // than the potentiometer value?
 if(currentMillis - previousMillis > tempoPotVal) {
  // save the last time we blinked the LED
  previousMillis = currentMillis;

  // if the LED is off turn it on and vice-versa
  if (tempoLEDState == LOW)
   tempoLEDState = HIGH;
  else
   tempoLEDState = LOW;

  // set the LED with the ledState of the variable
  digitalWrite(tempoLED, tempoLEDState);
 }
}

And here’s the breadboard + Arduino setup that goes with the code (click to embiggen):

It’s a potentiometer going to analog 0, an LED going to digital 12, a button going to digital 8 and an LED going to digital 7.  Note that the resistors I used with the LEDs can be anything suitable for the LEDs you are using…

So how was this right under our noses?  After wrestling with millis() for a while, I googled “blinking LED without delay” and found this reference on the Arduino site:

http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

I know.  It’s in the Arduino examples under “Digital”.

12 thoughts on “blinking LED without delay()

  1. Well I have tried this and I am afraid the result is exactly the same behaviour – maybe I am putting the tone generation in the wrong place so please check and give comments. I think the “blink with no delay” can work if the behaviour you want (ie a blink) doesn’t in itself actually require a time delay, but in the case of sound generation obviously you need a pause between each “note”….am I wrong or can someone shed other light on this! I will have a look at the other ideas Jukka and Tomi have suggested.

    // select the input pin for the potentiometer
    int potPin = A5;
    // variable to store the value coming from the pot
    int potVal = 0;
    // variable for tempo rate
    int tempo;
    // on-board LED shows us the delay of the main loop
    int loopLED = 13;
    int loopLEDState = LOW;

    // Tempo LEDs on analog pins
    const int TempoLed1 = A0;
    const int TempoLed2 = A1;
    const int TempoLed3 = A2;
    const int TempoLed4 = A3;

    // LED indicates the button toggle state
    const int buttonLED1 = 8;
    const int buttonLED2 = 9;
    const int buttonLED3 = 10;
    const int buttonLED4 = 11;

    // pushbutton to toggle the LED
    const int buttonPin1 = 2;
    const int buttonPin2 = 3;
    const int buttonPin3 = 4;
    const int buttonPin4 = 5;

    // variables for reading the led & pushbutton status
    int buttonState1 = LOW;
    int buttonState2 = LOW;
    int buttonState3 = LOW;
    int buttonState4 = LOW;
    // variable for previous button state
    int prevButtonState1 = LOW;
    int prevButtonState2 = LOW;
    int prevButtonState3 = LOW;
    int prevButtonState4 = LOW;

    // initial state and saved last state for button LED
    int buttonLEDState1 = LOW;
    int buttonLEDState2 = LOW;
    int buttonLEDState3 = LOW;
    int buttonLEDState4 = LOW;
    int prevButtonLEDState1;
    int prevButtonLEDState2;
    int prevButtonLEDState3;
    int prevButtonLEDState4;

    // initial state and saved last state for tempo LED
    int tempoLEDState1 = LOW;
    int tempoLEDState2 = LOW;
    int tempoLEDState3 = LOW;
    int tempoLEDState4 = LOW;

    // will store last time LED was updated
    long previousMillis = 0;

    void setup() {
    Serial.begin(9600);
    // declare the pins as either INPUT or OUTPUT:
    pinMode(loopLED, OUTPUT);

    pinMode(TempoLed1, OUTPUT);
    pinMode(buttonLED1, OUTPUT);
    pinMode(buttonPin1, INPUT);

    pinMode(TempoLed2, OUTPUT);
    pinMode(buttonLED2, OUTPUT);
    pinMode(buttonPin2, INPUT);

    pinMode(TempoLed3, OUTPUT);
    pinMode(buttonLED3, OUTPUT);
    pinMode(buttonPin3, INPUT);

    pinMode(TempoLed4, OUTPUT);
    pinMode(buttonLED4, OUTPUT);
    pinMode(buttonPin4, INPUT);
    // set buttons off to start
    digitalWrite(buttonPin1, LOW);
    digitalWrite(buttonPin2, LOW);
    digitalWrite(buttonPin3, LOW);
    digitalWrite(buttonPin4, LOW);
    }

    void loop() {

    // read the value from the button
    // (every time we go through the loop)
    buttonState1 = digitalRead(buttonPin1);
    buttonState2 = digitalRead(buttonPin2);
    buttonState3 = digitalRead(buttonPin3);
    buttonState4 = digitalRead(buttonPin4);

    // button is pressed
    // button 1
    if ((buttonState1 == 0) && (buttonState1 != prevButtonState1)) {
    buttonLEDState1 = (buttonLEDState1 == LOW) ? HIGH : LOW;
    digitalWrite(buttonLED1, buttonLEDState1);
    }
    prevButtonLEDState1 = buttonLEDState1;
    // set a new previous button state
    prevButtonState1 = buttonState1;

    // button 2
    if ((buttonState2 == 0) && (buttonState2 != prevButtonState2)) {
    buttonLEDState2 = (buttonLEDState2 == LOW) ? HIGH : LOW;
    digitalWrite(buttonLED2, buttonLEDState2);
    }
    prevButtonLEDState2 = buttonLEDState2;
    // set a new previous button state
    prevButtonState2 = buttonState2;

    // button 3
    if ((buttonState3 == 0) && (buttonState3 != prevButtonState3)) {
    buttonLEDState3 = (buttonLEDState3 == LOW) ? HIGH : LOW;
    digitalWrite(buttonLED3, buttonLEDState3);
    }
    prevButtonLEDState3 = buttonLEDState3;
    // set a new previous button state
    prevButtonState3 = buttonState3;

    // button 4
    if ((buttonState4 == 0) && (buttonState4 != prevButtonState4)) {
    buttonLEDState4 = (buttonLEDState4 == LOW) ? HIGH : LOW;
    digitalWrite(buttonLED4, buttonLEDState4);
    }
    prevButtonLEDState4 = buttonLEDState4;
    // set a new previous button state
    prevButtonState4 = buttonState4;

    // read the value from the potentiometer
    // (every time we go through the loop)
    potVal = analogRead(potPin);
    // map the min and max vals from the pot to the tempo rate
    tempo = map(potVal, 1023, 0, 500, 1);
    Serial.print(“Pot:”);
    Serial.print(potVal, DEC); // average reading to act as seperator between values
    Serial.print(” , “);
    Serial.print(“Tempo:”);
    Serial.println(tempo, DEC);
    // get the current time since arduino started running the program
    unsigned long currentMillis = millis();

    // is the difference now and the last time we blinked the LED bigger
    // than the potentiometer value?
    if(currentMillis – previousMillis > tempo) {
    // save the last time we blinked the LED
    previousMillis = currentMillis;

    // if the loopLED is off turn it on and vice-versa:
    if (loopLEDState == LOW){
    loopLEDState = HIGH;}
    else{
    loopLEDState = LOW;}

    digitalWrite(loopLED, loopLEDState);

    tempoRate ();

    }
    }

    void tempoRate() {

    // TEMPO
    if (buttonLEDState1 == HIGH){tone(7,300,tempo);}
    digitalWrite(TempoLed1, HIGH);
    digitalWrite(TempoLed2, LOW);
    digitalWrite(TempoLed3, LOW);
    digitalWrite(TempoLed4, LOW);
    delay(tempo);

    if (buttonLEDState2 == HIGH){tone(7,300,tempo);}
    digitalWrite(TempoLed1, LOW);
    digitalWrite(TempoLed2, HIGH);
    digitalWrite(TempoLed3, LOW);
    digitalWrite(TempoLed4, LOW);
    delay(tempo);

    if (buttonLEDState3 == HIGH){tone(7,300,tempo);}
    digitalWrite(TempoLed1, LOW);
    digitalWrite(TempoLed2, LOW);
    digitalWrite(TempoLed3, HIGH);
    digitalWrite(TempoLed4, LOW);
    delay(tempo);

    if (buttonLEDState4 == HIGH){tone(7,300,tempo);}
    digitalWrite(TempoLed1, LOW);
    digitalWrite(TempoLed2, LOW);
    digitalWrite(TempoLed3, LOW);
    digitalWrite(TempoLed4, HIGH);
    delay(tempo);

    }

  2. hmmm… hmmm… (stroking chin)
    i’m gonna try this out as soon as i have time, but meanwhile could you comment out all the Serial.print and Serial.println commands and see if that makes any difference?

  3. Hmmm. I’ll also look into this. We can at least develop this on monday when w next meet. Maybe the other suggestions are also worth looking.

  4. Yes, I can try commenting those serial.prints out but I don’t think it’ll help. Will keep investigating! By my way of understanding the “blink without delay” way will work for TWO parallel activities, but only if they don’t require timing. Of course it can be that I am missing something (like brain cells…)

  5. > like brain cells…
    lol, i know the feeling! 🙂

    at the moment, the main loop still has a delay, in the funciton tempoRate.

    i think the stuff in the function tempoRate needs to be inside the conditional statement: if(currentMillis – previousMillis > tempo) … so that it doesn’t use the delay as a timer, but the millis measurement instead.

    does that make sense? i can take a look at this tomorrow evening. what i will do is add a second toggle button and a second blinkin’ LED to the first code i posted. if i manage to get that working ok, will it help? i have a funny feeling i’m missing part of the puzzle…

    • Yes, I changed that to be like the original “blink without delay” example – you had two x delay(100); in your loop, so I thought removing them might help. I think what has to happen is to make each part a subroutine, so checking the buttons, states etc can be done for each button and led seperately, then each in turn is checked on consecutive loops of main loop….as you say, maybe gradually increasing no of buttons and leds. I tired this using your code, but what happened was that the tempo leds all flashed and tone played TOGETHER rather than with the time interval “tempo” — somehow the tempo has to be set using the millis() so first beat is at millis(), second at millis()+tempo, 3rd at millis()+2*tempo etc — maybe this is solution..?!

      • > so first beat is at millis(), second at millis()+tempo, 3rd at millis()+2*tempo etc

        andy, this is the route i was going to take! something like this, anyway, as i would have to experiment and see what it does with the software + hardware… just imagining it in the software is very difficult for me! 🙂 let me know if you try this!

        by the way: the delay(100) in my example was just to demonstrate that there was a delay in the loop (blinking the onboard LED) that wasn’t interfering with the milli() timing…

  6. I found this thread on Basic Stamp forum (I have some of these so was thinking I could use them if multitasking better supported, but not really…). But there is some useful coding logic explained below. I think this is also what the Arduino examples are explaining as well, it is just implementing this when it is not just 2 leds but 4X4X4…..

    http://forums.parallax.com/showthread.php?75851-Basic-Stamp-Multi-tasking

    “Sid’s point is that the BASIC Stamp is a single-tasking device, and SERIN will block all other activity. Thankfully — and as Sid pointed out in his post — the SERIN function has a timeout timer that will let you escape the SERIN if nothing arrives withing a given window.

    Beyond that, there are programming schemes that can make the BASIC Stamp (or any single-tasking controller) “appear” to be doing many things at once. The key is to setup a task-manager that handles important things through every program loop iteration, then divide the other task on each successive loop.”

    “The framework looks something like this:

    Code:

    Main:

    ‘ do “important” task here

    ON state GOSUB Task_0, Task_1, Task_2 …
    GOTO Main

    Task_0:

    ‘ task code

    IF (emergency) THEN
    GOSUB Emergency_Task
    ENDIF
    state = state + 1 // MaxStates
    RETURN

    As you can see, you put your important task in the top part of the main loop.· After it’s done, the program will call on a task based on the value of ‘state.’· Within each task subroutine, the task code is handle, then the next task is pointed to — unless there is some problem that needs to be dealt with first.· By breaking the program into small subroutine tasks, you can make things quite flexible and efficient.”

  7. I haven’t got the time to study your code or try it out, but it seems that the concurrency.cc programming environment for arduino might be worth the effort. Thet have pretty good tutorial pdf where they tell the basics and what I can gather there it’s pretty easy to do parallel programming, they have made own PAR function which when added basically does all the work for you and runs added code in parallel.
    The pdf can be found here: http://concurrency.cc/pdf/plumbing-for-the-arduino.pdf

  8. ok, i got something working! i hope you can do something with it:

    int potPin = A0; // select the input pin for the potentiometer
    int potVal = 0; // variable to store the value coming from the pot
    int tempoPotVal;

    // LED shows us the delay of the main loop
    // (on-board LED)
    int loopLED = 13;

    // LED shows us the tempo set by the pot
    int tempoLED1 = 7;
    int tempoLED2 = 9;
    int tempoLED3 = 11;
    int tempoLED4 = 12;

    // will store last time LED was updated
    long previousMillis = 0;

    // pushbutton to toggle the first led
    int buttonPin1 = 8;
    int buttonState1;
    int prevButtonState1 = 1;

    // pushbutton to toggle the second led
    int buttonPin2 = 10;
    int buttonState2;
    int prevButtonState2 = 1;

    // initial state and saved last states for button LED
    int buttonLEDState1 = HIGH;
    int prevButtonLEDState1;

    int buttonLEDState2 = HIGH;
    int prevButtonLEDState2;

    int buttonLEDState3 = HIGH;
    int prevButtonLEDState3;

    int buttonLEDState4 = HIGH;
    int prevButtonLEDState4;

    // array to store the button toggle states
    int buttArray[4] = {
    buttonLEDState1, buttonLEDState3,
    buttonLEDState2, buttonLEDState4};

    // array to store the tempo LEDs
    int tempoLEDArray[4] = {
    tempoLED1, tempoLED2,
    tempoLED3, tempoLED4};

    // the counter to pulse the LEDs
    int counter = 0;

    void setup() {
    // declare the pins as either INPUT or OUTPUT:
    pinMode(loopLED, OUTPUT);
    pinMode(tempoLED1, OUTPUT);
    pinMode(tempoLED2, OUTPUT);
    pinMode(tempoLED3, OUTPUT);
    pinMode(tempoLED4, OUTPUT);
    pinMode(buttonPin1, INPUT);
    pinMode(buttonPin2, INPUT);

    // set arduino’s internal resistor to pull down
    digitalWrite(buttonPin1, LOW);
    digitalWrite(buttonPin2, LOW);

    Serial.begin(9600);
    }

    void loop() {
    // this bit here just shows us how quickly the main loop is looping
    // it will run constantly
    digitalWrite(loopLED, HIGH);
    delay(100);
    digitalWrite(loopLED, LOW);
    delay(100);

    // read the value from the button
    // (every time we go through the loop)
    buttonState1 = digitalRead(buttonPin1);
    buttonState2 = digitalRead(buttonPin2);

    // button1 is pressed
    if ((buttonState1 == 0) && (buttonState1 != prevButtonState1)) {
    if (buttonLEDState1 == HIGH) {
    buttonLEDState1 = LOW;
    //digitalWrite(buttonLED1, buttonLEDState1);
    }
    else {
    buttonLEDState1 = HIGH;
    //digitalWrite(buttonLED1, buttonLEDState1);
    }
    buttArray[0] = buttonLEDState1;
    prevButtonLEDState1 = buttonLEDState1;
    }
    // set a new previous button state
    prevButtonState1 = buttonState1;

    // button2 is pressed
    if ((buttonState2 == 0) && (buttonState2 != prevButtonState2)) {
    if (buttonLEDState2 == HIGH) {
    buttonLEDState2 = LOW;
    //digitalWrite(buttonLED2, buttonLEDState2);
    }
    else {
    buttonLEDState2 = HIGH;
    //digitalWrite(buttonLED2, buttonLEDState2);
    }
    buttArray[1] = buttonLEDState2;
    prevButtonLEDState2 = buttonLEDState2;
    }
    // set a new previous button state
    prevButtonState2 = buttonState2;

    // button3 is pressed
    // button4 is pressed
    // these will be the same as buttons 1 and 2…
    // eventually can be made into a nice function 🙂

    // read the value from the potentiometer
    // (every time we go through the loop)
    potVal = analogRead(potPin);
    // map the min and max vals from the pot to the values we want to blink the LED
    tempoPotVal = map(potVal, 680, 0, 10, 1000);

    // get the current time since arduino started running the program
    unsigned long currentMillis = millis();

    // is the difference now and the last time we blinked the LED bigger than the potentiometer value?
    if (currentMillis – previousMillis > tempoPotVal) {
    // save the last time we blinked the LED
    previousMillis = currentMillis;

    // turn all the LEDs off – we do it this way because we don’t
    // need which one is on in order to turn it off
    for (int i=0; i<4; i++) {
    digitalWrite(tempoLEDArray[i], LOW);
    }

    // turn the current LED on
    // if its corresponding button is toggled on
    if (buttArray[counter] == HIGH) {
    digitalWrite(tempoLEDArray[counter], HIGH);
    }

    // add one to the counter, unless the max is reached, in which case,
    // reset the counter to 0
    if (counter < 3) {
    counter ++;
    }
    else {
    counter = 0;
    }

    }
    }

    so, again, the delay is just blinking the onboard LED so we can see that it’s independent from the tempo.

    what i do is pulse an LED each time the main loop is run, only the currentMillis-previousMillis makes the pause, just like in the “blink without delay” example. each time i run the main loop, i turn all the LEDs off and then add 1 to my counter, which selects the next LED in the tempoLEDArray to go on. so essentially it’s the same as blinking one LED, it’s just that the LED changes. does that make sense?

    anyhoo, we still listen for the button state (and toggle it when it’s pushed) but this is now incorporated into the tempoLED stuff. so each time it’s an LED’s turn to go on, we check (with the conditional statement) if the corresponding button is on. if it’s not on, we just don’t turn on that LED.

    man, i’m quite tired today, so i really hope i explained that ok. please let me know if it’s confusing! i didn’t really clean up the code as well as i wanted and i was too lazy to add two more buttons for testing.

    i also don’t have any debounce code in there…

    shall i make a little video?

  9. ok, video too big to post here – will email via wetransfer to you andy, with a copy of the code!

Comments are closed.