For loop w/o delay

How to write this using millis only(no delay).

int led = 13;

void setup() {
  pinMode(led, OUTPUT);
}


void loop() {
  for (int i=0; i<5; i++) {
    digitalWrite(led, HIGH);
    delay(250);
    digitalWrite(led, LOW);
    delay(250);
  }
  delay(1000);
}
1 Like

Have you managed to blink an LED without the blink five times and then pause a bit part of what your current sketch does?

Have you experimented with the "blink without delay" sketch on offer in the IDE?

You can also google

blink without delay arduino

and see if any of very many hits explains the basic technique for using millis() instead of dealy().

If you can do that, counting five blinks and adjusting the timing until the sixth and so forth might be easy.

a7

1 Like

non-blocking timing tutorials:
Blink without delay().
Blink without delay detailed explanation
Beginner's guide to millis().
Several things at a time.

Try this code:

int led = 13;
unsigned long myTime1;
unsigned long myTime2;
//----------------------------------------------------------
void setup() {
  pinMode(led, OUTPUT);
  myTime2 = millis();
}
//----------------------------------------------------------
void loop() {
  if (millis() - myTime2 > 1000)
  {
    myTime1 = millis();
    digitalWrite(led, HIGH);
    for (int i = 0; i < 9; ) {
      if (millis() - myTime1 > 250)
      {
        digitalWrite(led, !digitalRead(led));
        i++;
        myTime1 = millis();
      }
    }
     myTime2 = millis();
  }
}
1 Like

and another..

int led = 13;
unsigned long lastLoop;
unsigned long lastBlink;
byte blinks = 0;

void setup() {
  pinMode(led, OUTPUT);
}


void loop() {

  unsigned long now = millis();

  if (now - lastLoop >= 1000)
  {
    if ( now - lastBlink >= 250)
    {
      digitalWrite(led, !digitalRead(led));
      lastBlink = now;
      blinks++;
      if (blinks == 10)
      {
        blinks = 0;
        lastLoop = now;
      }
    }
  }
  
  /*
  for (int i = 0; i < 5; i++) {
    digitalWrite(led, HIGH);
    delay(250);
    digitalWrite(led, LOW);
    delay(250);
  }
  delay(1000);
  */
}

have fun.. ~q

My take:

const byte ledPin = 13;

const byte numFlashes = 10; // 10 toggles for 5 on, 5 off.
const unsigned long loopInterval = 1000;
const unsigned long ledInterval = 250;
bool flashing = false;
byte flashIndex = 0;

void setup()
{
   Serial.begin(115200);
   pinMode(ledPin, OUTPUT);
}

void loop()
{
   static unsigned long loopTimer = 0;
   if (millis() - loopTimer >= loopInterval && flashing == false)
   {

      flashIndex = 0;
      flashing = true;
   }

   static unsigned long ledTimer = 0;
   if (millis() - ledTimer >= ledInterval && flashing == true)
   {
      ledTimer = millis();
      digitalWrite(ledPin, !digitalRead(ledPin));
      flashIndex++;
      if (flashIndex > numFlashes)
      {
         digitalWrite(ledPin, LOW);
         flashIndex = 0;
         loopTimer = millis();
         flashing = false;
      }
   }
}


1 Like

It's a generalized problem. You can "unblock" a for loop using a state machine, but how?

This is the basis:

Here is how you implement a for loop using a state machine:

for(i=3; i<6; i++) {<somecode>}

decomposes as the sequence

i=3;
while (i<6) {
<somecode>
i++;
}

For the state machine you would do something like

if (state == INITIALIZE) {
 i=3;
 state = RUN;
}
else if (state == RUN and i<6) {
 <somecode>
 i++;
}
else {
 state = IDLE;
}

To activate it, you just assert:

state = INITIALIZE;

Or, you can use a switch case statement:

switch (state) {

case:INITIALIZE
   i=3;
   state = RUN;
   break;

case:RUN
 if (i<6) {
   <somecode>
   i++;
    }
  else
    {
    state = IDLE;
    }
   break;

case: IDLE
  break;
}

Sorry about the multiple edits - this post was created for the old forum software, I had to convert...

1 Like

What they're all saying is you can't write a for loop like that without delay.
Or, it seems, without code tags.

Hello jovosm

Welcome to the worldbest Arduino forum ever.

I also have a small example.

// https://forum.arduino.cc/t/for-loop-w-o-delay/1152722
// https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "For loop w/o delay"
// make variables 
constexpr uint8_t Led {13};
// make application
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  pinMode(Led,OUTPUT);
  digitalWrite(Led,HIGH);
}
void loop() 
{
  uint32_t currentMillis=millis(); 
  static uint32_t previousMillis=0;
  const uint32_t intervalMillis[] {250,250,250,250,250,250,250,250,250,1250};
  static uint8_t element=0;
  if (currentMillis-previousMillis>=intervalMillis[element]) 
  {
    previousMillis=currentMillis;
    element=(element+1)%(sizeof(intervalMillis)/sizeof(intervalMillis[0]));
    digitalWrite(Led,digitalRead(Led)?LOW:HIGH);
  }
}

Take the best one for your presentation at school.

Have a nice day and enjoy coding in C++.

1 Like

OK, everyone's having fun.

# define theLED 13

void setup() {
  pinMode(theLED, OUTPUT);
}

unsigned long ticker, phase;

void loop() {
  ticker = millis() / 250;
  phase = ticker % 14;

  if (phase < 11)
    if (phase & 1) digitalWrite(theLED, HIGH);
    else digitalWrite(theLED, LOW);
}

a7

5 Likes

Excellent solution :+1:

indeed very compact solution

@alto777 can you explain the math behind it or post a solution that is calculating blinkfreqeuency and pausing time based on user-changeable constants?

Here's a version that makes the mathematics abundantly clear

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, ((millis() / 250 % 14 + 0x16) & 17) == 0x11);
}

a7

2 Likes

Untested!

const int led = 13;
int counter = 0;
const int blinks = 5;
unsigned long lastMillis = 0;
const unsigned long blinkDelay = 250;
const unsigned long extraDelay = 1000;
unsigned long thisDelay = blinkDelay;

void setup() {
  pinMode(led, OUTPUT);
  digitalWrite(led, HIGH);
}

void loop() {
  if (millis() - lastMillis >= thisDelay) {
    lastMillis = millis();
    if (digitalRead(led) == HIGH) {
      digitalWrite(led, LOW);
      if (counter == blinks-1) thisDelay += extraDelay;
    }
    else {
      digitalWrite(led, HIGH);
      counter++;
      if (counter >= blinks) {
        counter = 0;
        thisDelay = blinkDelay;
      }
    }
  }
}

@PaulRB I tested your code and found it to function plausibly.

I timed that for fun. It took me 70 seconds to copy your code, place it in a simulation, wire an LED so I could see it better and then compile and execute the code.

Also I have an UNO connected semi-permanently, it already has a nice extra LED on pin 13. I did not measure but it could not have taken a minute to test it IRL on that hardware.

Now maybe I'll look at your code. :expressionless:


I've been seeing more subtle miscastings of millis() in the service of eliminating delay(), not saying this is the case with any of the examples here, but I have become less confident in my own ability to sling together things like this, and more surprised about how a dropped millisecond here or there (or other flaw) can, over sometimes not that very long a time, start to "misbehave".

So let's all be careful out there. Testing something like these examples for correctness is probably harder than writing them in the first palce.


The two hacks @alto777 visited upon us were, I assume, for your amusement. They are one bad and the other horrible. I hate tricks like that.

They both can be generalized a bit, but are far from suitable should the requirements change even a little bit. Some of the other examples may be a bit more flexible.

The code in #10 can support only changes in the number of blinks and the length of the pause between groups of blinks, and can be changed to operate faster or slower. Blink duty cycle is frozen at 50 percent, and all timing is based on units of N ms, hard coded here at 250.

The method in #13 is a bit more flexible algorithmically, but quite tricky to flex. An obvious (!) extension to the way it works would allow a fixed duty cycle of 1/2^N, that is to say 50, 25, 12.5 percent and so forth.

I now am thinking that #10 could be altered to allow similar change to the duty cycle.

All exercises for the reader. Or not. Srsly, I would say go to the beach instead.

a7

2 Likes

Enjoy the day and have fun :grinning:

@alto777 is clever

int led = 13;
int cnt = 1;

unsigned long msecPeriod = 150;
unsigned long msecLst;

void setup() {
  pinMode (led, OUTPUT);
  digitalWrite (led, HIGH);
}

void loop() {
    unsigned long msec = millis ();
    if (msec - msecLst >= msecPeriod)  {
        msecLst = msec;
        if (LOW == digitalRead (led))
            if (5 == cnt++)  {
                digitalWrite (led, LOW);
                msecPeriod = 1000;
                cnt        = 0;
            }
            else {
                msecPeriod = 250;
                digitalWrite (led, HIGH);
            }
        else
            digitalWrite (led, LOW);
    }
}

abundantly specific no longer interpretable.

Though it depends on the knowledge about what each part means
and for me personal it will take a longer time to understand it because I'm not familiar with this kind of calculations.

So there is still air upwards regarding easyness to understand.

best regards Stefan

Might as well make it millis() / 256 and avoid the hiccup at overflow.

2 Likes

Seems to be the cause of a little startup transient. Make it 777 and see.

Both 250 and 1000 should be manifest constants, then this wouldn't have cropped up.

int flash = 250;
int pause = 1000;

unsigned long msecPeriod = flash;
unsigned long msecLst;

and so forth.

Another thing I would change is to add a real variable instead of reading back the output pin to determine the LED state. I can't imagine that it wouldn't always be possible to do that (read the output register), though. It is something I've done more recently, usually in quick hacks as a convenience.

I should have sarcasm tags. The second version is a nightmare, and the use of hex, decimal and octal constants was intentionally obfuscatory.

Both my versions will yield to analysis. One is only one line of code, so how hard could it be?

I also assumed you were coaxing an explanation/elaboration on a pedagogical basis. TBH I don't think anyone should be taught those tricks, but I do think it would be fun to figure out. My hope is that code like that would never make it into any serious piece of software.

Agree. That would use a shift operation instead of division, added to '&' and '%', which are arguably not always among the first few handfuls of knowledge someone picks up or is taught learning C, so a win-win.

I knew something would happen 49.7 days out. I did not think about what that might be. And for human consumption, the two percent difference in the period would be negligible. Maybe it would be closer on Arduinos on the high end of their lousy clocks.

I have said the second version is horrible. Here's a version that is still bad, with a few comments that I hope are not misleading.

# define TICK   200     // basic step size milliseconds
# define LENGTH 32      // number of TICKS for entire sequence
# define OFFSET 108     // move the index close to 128 (0x80)
# define MASK   0x43    // light the LED if we are under 0x40 and at 0x3 of the low two bits count of 4 (25 % duty cycle);

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, ((millis() / TICK % LENGTH + OFFSET) & MASK) == MASK);
}

The first steps up to LENGTH are OFFSET so we can use a high bit to mean we are in the flashing portion of the sequence, the bottom 2 bits used further to make the flash on/off. Once we get to where that high bit (0x40) is clear, we are in the pause portion.

I will say again I don't like clever stuff like this. A warning for anyone is a special feeling of accomplishment when a few lines of code does something clever or obscure - ask yourself if even you will be able to read it in the future. Here, a future when I mightn't understand what I did yesterday is tomorrow or even today.

Basically it's like indexing a table at an offset. There is no table, as each entry is equal to the index.

if it's time (TICK loop throttle)
    onOff = table[counter];
    counter++;
    if (counter >= LENGTH) counter = 0;

    digitalWrite(theLED, onOff);

OK, I've been playing with the adjustable version above. It is. Adjustable. But evidently adjustments are fraught, and can only be done correctly every time by someone who understands whats going on better than I. :expressionless:

a7