Timing problem with millis

Hi, I'm new to Arduino programming i tried to make the Arduino control two LEDs by gradually turning up the brightness of said LEDs to the desired amount and then gradually turning it down in a loop
if the speed of the two LEDs are the same the program works correctly but when one of the LEDs is half the speed of the other one they desynchronize after a bit of time is there a way to fix this?

const int whiteLed = 2;
const int blueLed = 3;

long prevWhiteTimeMillis = 0;
boolean whiteState = false;

long prevBlueTimeMillis = 0;
boolean blueState = false;

void setup() {
  // put your setup code here, to run once:
  pinMode(whiteLed, OUTPUT);
  pinMode(blueLed, OUTPUT);

}
int timer = 0;
void loop() {
  whiteLedControl();
  blueLedControl();
}

int whiteLedBrightness = 0;
int whiteLedMaxBrightness = 50;
boolean whiteLedSwitch = false;

void whiteLedControl() {
  if (millis() - prevWhiteTimeMillis >= 500 / 25) {
    if (whiteLedBrightness >= whiteLedMaxBrightness) {
      whiteLedSwitch = true;
    }
    if (whiteLedBrightness <= 1) {
      whiteLedSwitch = false;
    }

    if (!whiteLedSwitch) {
      if (whiteLedBrightness <= whiteLedMaxBrightness) {
        whiteLedBrightness++;
      } else {
        whiteLedSwitch = true;
      }
    } else {
      if (whiteLedBrightness >= 0) {
        whiteLedBrightness--;
      } else {
        whiteLedSwitch = false;
      }
    }
    prevWhiteTimeMillis = millis();
  }
  analogWrite(whiteLed, whiteLedBrightness);
}

int blueLedBrightness = 0;
int blueLedMaxBrightness = 50;
boolean blueLedSwitch = false;

void blueLedControl() {
  if (millis() - prevBlueTimeMillis >= 500 / 50) {
    if (blueLedBrightness >= blueLedMaxBrightness) {
      blueLedSwitch = true;
    }
    if (blueLedBrightness <= 1) {
      blueLedSwitch = false;
    }

    if (!blueLedSwitch) {
      if (blueLedBrightness <= blueLedMaxBrightness) {
        blueLedBrightness++;
      } else {
        blueLedSwitch = true;
      }
    } else {
      if (blueLedBrightness >= 0) {
        blueLedBrightness--;
      } else {
        blueLedSwitch = false;
      }
    }
    prevBlueTimeMillis = millis();
  }
  analogWrite(blueLed, blueLedBrightness);
}

thanks to user @Delta_G the problem was in this line

prevBlueTimeMillis = millis();

because the millis() function does not run every millisecond exactly it causes problems
this is the code working now

const int whiteLed = 2;
const int blueLed = 3;

long prevWhiteTimeMillis = 0;
boolean whiteState = false;

long prevBlueTimeMillis = 0;
boolean blueState = false;

unsigned long currentMillis;

void setup() {
  // put your setup code here, to run once:
  pinMode(whiteLed, OUTPUT);
  pinMode(blueLed, OUTPUT);

}
int timer = 0;
void loop() {
  currentMillis = millis();
  blueLedControl();
  whiteLedControl();
}

int whiteLedBrightness = 0;
int whiteLedMaxBrightness = 50;
boolean whiteLedSwitch = false;

void whiteLedControl() {
  if (currentMillis - prevWhiteTimeMillis >= 500 / 25) {
    if (whiteLedBrightness >= whiteLedMaxBrightness) {
      whiteLedSwitch = true;
    }
    if (whiteLedBrightness <= 1) {
      whiteLedSwitch = false;
    }

    if (!whiteLedSwitch) {
      if (whiteLedBrightness <= whiteLedMaxBrightness) {
        whiteLedBrightness++;
      } else {
        whiteLedSwitch = true;
      }
    } else {
      if (whiteLedBrightness >= 0) {
        whiteLedBrightness--;
      } else {
        whiteLedSwitch = false;
      }
    }
    prevWhiteTimeMillis += 500 / 25;
  }
  analogWrite(whiteLed, whiteLedBrightness);
}

int blueLedBrightness = 0;
int blueLedMaxBrightness = 50;
boolean blueLedSwitch = false;

void blueLedControl() {
  if (currentMillis - prevBlueTimeMillis >= 500 / 50) {
    if (blueLedBrightness >= blueLedMaxBrightness) {
      blueLedSwitch = true;
    }
    if (blueLedBrightness <= 1) {
      blueLedSwitch = false;
    }

    if (!blueLedSwitch) {
      if (blueLedBrightness <= blueLedMaxBrightness) {
        blueLedBrightness++;
      } else {
        blueLedSwitch = true;
      }
    } else {
      if (blueLedBrightness >= 0) {
        blueLedBrightness--;
      } else {
        blueLedSwitch = false;
      }
    }
    prevBlueTimeMillis += 500 / 50;
  }
  analogWrite(blueLed, blueLedBrightness);
}

I can't see right away why they get out of sync.

But the easiest way to do one thing twice as fast as the other is to have one timing section instead of two, and then within that section do the half fast thing only every other time. Do the fully fast thing every time.

You can use a simple counter for this

// every N milliseconds ... 
  static byte counter;

  counter++;
  if ((counter % 2) == 1) {  // number is odd?
     doHalfFastThing();
  }

  doFullyFastThing();

Since one will be done exactly twice as often, they cannot get out of sync unless the times are not just a factor of two different.

Which makes me curious again about why your code does. Get out of sync.

a7

Your sketch in Wokwi simulation: Timing problem with millis - Wokwi ESP32, STM32, Arduino Simulator

I tried that and didn't work for me

const int whiteLed = 2;
const int blueLed = 3;

long prevWhiteTimeMillis = 0;
boolean whiteState = false;

long prevBlueTimeMillis = 0;
boolean blueState = false;

unsigned long currentMillis;

void setup() {
  // put your setup code here, to run once:
  pinMode(whiteLed, OUTPUT);
  pinMode(blueLed, OUTPUT);

}
int timer = 0;
void loop() {
  currentMillis = millis();
  blueLedControl();
  whiteLedControl();
}

int whiteLedBrightness = 0;
int whiteLedMaxBrightness = 50;
boolean whiteLedSwitch = false;

void whiteLedControl() {
  if (currentMillis - prevWhiteTimeMillis >= 500 / 25) {
    if (whiteLedBrightness >= whiteLedMaxBrightness) {
      whiteLedSwitch = true;
    }
    if (whiteLedBrightness <= 1) {
      whiteLedSwitch = false;
    }

    if (!whiteLedSwitch) {
      if (whiteLedBrightness <= whiteLedMaxBrightness) {
        whiteLedBrightness++;
      } else {
        whiteLedSwitch = true;
      }
    } else {
      if (whiteLedBrightness >= 0) {
        whiteLedBrightness--;
      } else {
        whiteLedSwitch = false;
      }
    }
    prevWhiteTimeMillis = currentMillis;
  }
  analogWrite(whiteLed, whiteLedBrightness);
}

int blueLedBrightness = 0;
int blueLedMaxBrightness = 50;
boolean blueLedSwitch = false;

void blueLedControl() {
  if (currentMillis - prevBlueTimeMillis >= 500 / 50) {
    if (blueLedBrightness >= blueLedMaxBrightness) {
      blueLedSwitch = true;
    }
    if (blueLedBrightness <= 1) {
      blueLedSwitch = false;
    }

    if (!blueLedSwitch) {
      if (blueLedBrightness <= blueLedMaxBrightness) {
        blueLedBrightness++;
      } else {
        blueLedSwitch = true;
      }
    } else {
      if (blueLedBrightness >= 0) {
        blueLedBrightness--;
      } else {
        blueLedSwitch = false;
      }
    }
    prevBlueTimeMillis = currentMillis;
  }
  analogWrite(blueLed, blueLedBrightness);
}

even if this works it will lose the option of any timing that i would want

Arduino mega 2560

they are not flashing the brightness changes on those timings

the LEDs start synced together meaning they both start at the same time and their brightness oscillates
the blue LED oscillates double the speed of the white one so every other time that the blue LED starts it should start with the white one
they do that at first but after a while the blue LED falls behind

each cycle (the rise and the fall of the LED's brightness) of the white LED takes 2 seconds and the blue LED takes a second so it's pretty obvious by watching

@morteza20deris It took some before we understood the syncing problem, but I think that Delta_G is right.

If two timings are related, then I would use a single millis-timer.
Since the led is seen by the human eye, it is possible to run a millis-timer at a fixed rate (something 20...100Hz) and deal with the brightness in the millis-timer.

I have updated the Wokwi simulation to a Mega board: Timing problem with millis - Wokwi ESP32, STM32, Arduino Simulator

This is the sync problem after 2 minutes:
afbeelding

2 Likes

BTW

long prevWhiteTimeMillis = 0;

long prevBlueTimeMillis = 0;

  • These should be unsigned long.

What is even worse, occasionally millis() increments by 2 instead of 1 (because the millis() interrupt occurs at 1.024mS intervals instead of exactly 1mS). This would eventually throw off the timing because the shorter delay would have a higher probability of hitting the increment by 2.

That would have been my suggestion.

1 Like

this fixed it
it was exactly as you said it is
the two LEDs are now perfectly in sync as far as my eyes can see
thanks a lot you are a brilliant person

1 Like

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