How to remove delay() in a for loop

Dear all,

I am a new Arduino user and I am trying to learn something by my own.

Can you tell me how to get rid of delay() in the for loop of the following sketch?

My problem is that I would like to add a debounced button (debounced used millis()) to the sketch such that, when I push the button, a LED ligths up gradually (updating the brightness every 10 ms) from dark to very bright and then it goes back, with this process repeating. When I repush the button again, the LED should switch off. With the delay() inside the sketch, the program blocks.

Thanks a lot for your precious help!

const int LED=9;   // Define LED for Pin 9
void setup()
{
  pinMode (LED, OUTPUT);    // Set the LED pin as an output
}

void loop()
{
  for (int i=0; i<256; i++)
  {
    analogWrite(LED, i);
    delay(10);
  }
  for (int i=255; i>=0; i--)
  {
    analogWrite(LED, i);
    delay(10);
  }
}

The simple answer is simply delete it

The more complicated answer is rewrite your code without a for loop.

Try here

1 Like

Don't use a for loop

Let the loop() function do the looping for you, incrementing

An example for you to experiment with

unsigned long currentMillis;
unsigned long startMillis;
unsigned long period = 1000; //change this to suit requirements
int count = 0;

void setup()
{
  Serial.begin(115200);
  startMillis = millis();
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - startMillis >= period)
  {
    Serial.println(count);
    count++;
    startMillis = currentMillis;
  }
}
1 Like

How you do this is to use some persistent variables (either local or global) to remember what you are doing rather than trapping the process in a for loop to remember what you are doing. Then you can use the outside loop() function to iterate through everything.

void loop()
{
  // local persistent state variables:
  static unsigned long lastChange = 0; 
  static int i = 0;  // how bright
  static int inc = +1; // waxing or waning 
  // Check if it's time to do something
  if(millis() - lastChange < 10 )
     return;  // too early 
  // else do what needs doing
  analogWrite(LED, i);  
  i += inc; 
  // Check for valid state
  if (i > 255){
    i = 255;
    inc *= -1;
  }
  if(i < 0){
    inc *= -1;
    i = 0;
  } 
}

Once you've done that for one task, if you want to add more tasks, you can change the name of that function to something else, (for example, pulseLED) and then call it from a new outer loop:

void loop(){
   pulseLED();
   checkButton();
   reportToSerialMonitor();
   //...
}

You might add another similar function to read your button and update a global on/off state that an updated pulseLED() function would need to respect.

image

1 Like

The examples shown above are good for this particular case but a more general solution is a Finite State Machine. Here is your original sketch re-written as a Finite State Machine. Note that each 'delay' is now a place where the state has a millis() timer. The state is repeated every time through loop() (thousands of times per second) and when the counting is done, switches to a different state.

enum States {CountingUp, CountingDown} State = CountingUp;
int Brightness = 0;
unsigned long StateStartTime = 0;

const int LED = 9; // Define LED for Pin 9
void setup()
{
  pinMode (LED, OUTPUT);    // Set the LED pin as an output
}

void loop()
{
  switch (State)
  {
    case CountingUp:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness++;
        if (Brightness > 255)
        {
          Brightness = 255;
          State = CountingDown;
        }
      }
      break;

    case CountingDown:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness--;
        if (Brightness < 0)
        {
          Brightness = 0;
          State = CountingUp;
        }
      }
      break;
  }
}

This is a finite state machine customized for this particular case, but it doesn't point towards more complicated programs. You may need to have several semi-independent FSMs, for example, one for the LEDs marching around a jukebox, one for the MP3 player, another handling serial communication, and maybe another handling coinage. They don't all need to be global state variables with the processing flattened into a single Markovized state space. The only state variables that should be global are ones that are necessary to communicate between FSMs.

In @lucone's example, it sounds like they'd like to add functionality like https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce

I'd modify that example's FSM's loop() into it's own checkButton() function, leave ledVariable as a global SV, then use that to control pulseLED().

Thanks a lot, John!!!

Thanks a lot Dave!!!

Dear John,

I have tried to elaborate starting from your sketch. What I wanted to do is to add a button to your sketch such that: (i) when I push the button, the LED start fading according to your sketch and (ii) when I push the buttn again, the LED switches off.

Unfortunately, it does not work! I attach the code as follows. I would be extremely grateful if you could give me some indications of why it does not work. Thanks a lot!

enum States {CountingUp, CountingDown} State = CountingUp;
int Brightness = 0;
unsigned long StateStartTime = 0;

const int LED = 9; // Define LED for Pin 9
const int BUTTON = 2;
boolean BUTTONstatus = LOW;
boolean LEDstatus = LOW;
int waitDebounce = 50;
unsigned long lasttimeDebounce = 0;
boolean lastread = LOW;
void setup()
{
  pinMode (LED, OUTPUT);    // Set the LED pin as an output
  pinMode (BUTTON, INPUT);
  digitalWrite(LED, LEDstatus);
}

void loop()
{
  boolean read = digitalRead(BUTTON);
  if(read != lastread){
    lasttimeDebounce = millis();
  }
  if((millis() - lasttimeDebounce) > waitDebounce){
    if(read != BUTTONstatus && read == HIGH){
    switch (State)
  {
    case CountingUp:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness++;
        if (Brightness > 255)
        {
          Brightness = 255;
          State = CountingDown;
        }
      }
      break;

    case CountingDown:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness--;
        if (Brightness < 0)
        {
          Brightness = 0;
          State = CountingUp;
        }
      }
      break;
  } 
  }
  BUTTONstatus = read;
  }
  lastread = read;
  }

The FSM has to repeat very often. What you have done is changed the sketch to run the FSM one step each time the button input goes from LOW to HIGH. That is NOT going to be quick.

void loop()
{
  boolean read = digitalRead(BUTTON);
  if (read != lastread)
  {
    lasttimeDebounce = millis();
  }
  if ((millis() - lasttimeDebounce) > waitDebounce)
  {
    if (read != BUTTONstatus && read == HIGH)
    {
      switch (State)
      {

Your original design was "fade up to full brightness, then fade down to off, and repeat". It is not clear exactly what you want you want the button to do. Did you want the button to switch between "OFF" and "fade up and down"? Did you want the OFF to happen immediately whatever the current brightness is? Did you want it to fade out from whatever the current brightness is? Did you want it to complete the current cycle until it reaches OFF and then not fade up again?

Describe what you want the light to do in more detail.

Sorry for not being clear!

I use a debounced button to switch between "fade up and down" and "off":

  • the led starts switched off;

  • when the button is clicked the first time, the led starts fading up and down according to your code;

  • when the button is clicked the second time, the led swithces off again;

  • reclicking the button restart the led.

Thanks a lot for your amazing and precious help!

So you want to turn the effect on and off. Rather than have each step of the effect wait for a button press, use a flag to keep track of whether the effect is on or off. The button controls the flag. The flag controls the effect.

enum States {CountingUp, CountingDown} State = CountingUp;
int Brightness = 0;
unsigned long StateStartTime = 0;

const int LED = 9; // Define LED for Pin 9
const int BUTTON = 2;
boolean LEDState = false;
unsigned waitDebounce = 50;
unsigned long lasttimeDebounce = 0;
boolean lastread = LOW;

void setup()
{
  pinMode (LED, OUTPUT);    // Set the LED pin as an output
  pinMode (BUTTON, INPUT);
  analogWrite(LED, 0);
}

void loop()
{
  boolean read = digitalRead(BUTTON);
  if (read != lastread
      && millis() - lasttimeDebounce > waitDebounce)
  {
    // State just changed
    lastread = read;
    lasttimeDebounce = millis();
    if (read == HIGH)
    {
      // State just changed to HIGH
      LEDState = !LEDState;
    }
  }

  if (LEDState)
    FadeUpAndDown();
  else
    analogWrite(LED, 0);
}

void FadeUpAndDown()
{
  switch (State)
  {
    case CountingUp:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness++;
        if (Brightness > 255)
        {
          Brightness = 255;
          State = CountingDown;
        }
      }
      break;

    case CountingDown:
      if (millis() - StateStartTime >= 10)
      {
        StateStartTime = millis();
        analogWrite(LED, Brightness);
        Brightness--;
        if (Brightness < 0)
        {
          Brightness = 0;
          State = CountingUp;
        }
      }
      break;
  }
}
1 Like

Amazing!!! Thanks a lot!!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.