For-loop iterator not incrementing

Hello,

The for-loop in 2a is not iterating, but the while-loop in 2b is. Can someone explain why?

This is for a school assignment where you have to make the LED blink, starting with a blink rate of 10ms and slowing down until it reaches 2000ms.

const byte pinLed = 13;
const unsigned long fastestBlinkRate = 10;    // 10ms
const unsigned long slowestBlinkRate = 2000;  // 2000ms
unsigned long timeNow;
unsigned long timeLast;
bool toggle;

void setup() {
  pinMode(pinLed, OUTPUT);
  timeNow = timeLast = 0;
  toggle = false;
}

void loop() {
  // 2a
  for (unsigned long i = fastestBlinkRate; i < slowestBlinkRate; i += (unsigned long)(i/8)) {
    timeNow = millis();
    if (timeNow - timeLast >= i) {
      toggle ? digitalWrite(pinLed, HIGH) : digitalWrite(pinLed, LOW);
      toggle = !toggle;
      timeLast = timeNow;
    }
  }

  // 2b
  unsigned long i = fastestBlinkRate;
  while (i < slowestBlinkRate) {
    timeNow = millis();
    if (timeNow - timeLast >= i) {
      toggle ? digitalWrite(pinLed, HIGH) : digitalWrite(pinLed, LOW);
      toggle = !toggle;
      timeLast = timeNow;
      i += (unsigned long)(i/8);
    }
  }
}

In 2a, your i += (unsigned long)(i/8) is evaluated each time through the loop, so your loop completes very quickly without if (timeNow - timeLast >= i) becoming true, which gives you the impression that it's not iterating, but it is.

If does, only you're not seeing it :wink:

The for loop will continue unconditionally and very fast. It will NOT wait for the if-statement to be true or not. And because that's only true on a ms scale (which is slooooowwwww) the forloop will end without any of the if-statements being true :wink:

This has a link with non-blocking code, which is good (because it gives you the time to do other stuff). The while-loop in 2b blocks everything which is a pitty. It's blocked in the loop, checking the statement millions of times without doing anything. Good practice is to only use loops in places you know they are quickly. If the task isn't quick, just let the loop() function take care of the looping and just come back the next time. Aka, 2a is the better basis only you need:

const byte LedPin = 13;
const unsigned long FastestBlinkRate = 10;    // 10ms
const unsigned long SlowestBlinkRate = 2000;  // 2000ms

//unsigned long timeNow; //not needed as global!
unsigned long timeLastBlink;
bool toggle; //can also just read this ;)
unsigned long currentBlinkRate = FastestBlinkRate;

void setup() {
  pinMode(LedPin, OUTPUT);
  //toggle = false; //you can do this when declaring the variable. And a global is 0 / false by default
}

void loop() {
  // 2a
  unsigned long timeNow = millis();
  if (timeNow - timeLastBlink >= currentBlinkRate) {
    //save time for next run
    timeLastBlink = timeNow;
    
    //toggle led
    toggle = !toggle;
    digitalWrite(LedPin, toggle);
    
    if(currentBlinkRate <= FastestBlinkRate){
      //increment speed
      currentBlinkRate += currentBlinkRate / 8;
    }
  }
}

Some tips:

toggle ? digitalWrite(pinLed, HIGH) : digitalWrite(pinLed, LOW);
//is just a complicated way of writing
digitalWrite(pinLed, toggle);
i += (unsigned long)(i/8);
//No need to cast
i += i/8;

Instead of using a toggle variable you can just read the current state. Boils it down to an easy function

void digitalToggle(byte p){
  digitalWrite(p, !digitalRead(p));
}

I would recommend that you use some Serial.print() statements to see what values your are getting for each variable. Then you will truly understand how the program is flowing, and whether it is iterating or not.
My guess is that it is iterating, but not evaluating to True - as others have already mentioned.

arduarn:
In 2a, your i += (unsigned long)(i/8) is evaluated each time through the loop, so your loop completes very quickly without if (timeNow - timeLast >= i) becoming true, which gives you the impression that it's not iterating, but it is.

septillion:
If does, only you're not seeing it :wink:

The for loop will continue unconditionally and very fast. It will NOT wait for the if-statement to be true or not. And because that's only true on a ms scale (which is slooooowwwww) the forloop will end without any of the if-statements being true :wink:

This has a link with non-blocking code, which is good (because it gives you the time to do other stuff). The while-loop in 2b blocks everything which is a pitty. It's blocked in the loop, checking the statement millions of times without doing anything. Good practice is to only use loops in places you know they are quickly. If the task isn't quick, just let the loop() function take care of the looping and just come back the next time. Aka, 2a is the better basis only you need:

...

Hmm, don't really understand what you guys mean by that :confused: because it's checking the if-statement which gets evaluated to true every time (and prints 10).

Here's the code doing that:

const byte pinLed = 13;
const unsigned long fastestBlinkRate = 10;    // 10ms
const unsigned long slowestBlinkRate = 2000;  // 2000ms

void setup() {
  Serial.begin(9600);
  pinMode(pinLed, OUTPUT);
}

void loop() {
  // 2a
  bool toggle = false;
  unsigned long timeLast = 0;
  unsigned long timeNow = 0;
  for (unsigned long i = fastestBlinkRate; i < slowestBlinkRate; i += (unsigned long)(i/8)) {
    timeNow = millis();
    if (timeNow - timeLast >= i) {
      toggle ? digitalWrite(pinLed, HIGH) : digitalWrite(pinLed, LOW);
      toggle = !toggle;
      timeLast = timeNow;
      Serial.println(i);
    }
  }

  // 2b
//  unsigned long i = fastestBlinkRate;
//  while (i < slowestBlinkRate) {
//    timeNow = millis();
//    if (timeNow - timeLast >= i) {
//      toggle ? digitalWrite(pinLed, HIGH) : digitalWrite(pinLed, LOW);
//      toggle = !toggle;
//      timeLast = timeNow;
//      i += (unsigned long)(i/8);
//    }
//  }
}

In "Loop 2a", try putting:

Serial.println(i);

right before this line:

if (timeNow - timeLast >= i) {

Then come back and tell us the "iterator is not incrementing"