I have a sketch simulating a bicyle tail light. When I click button, led starts blinking. When I click again, it stops blinking.
If I turn blinking on and off without taking too long, everything works fine. However, if I let the blinking off for more than a few dozens of seconds, the next time I press the button it takes lots of seconds for the led to start blinking again.
I cannot imagine why this should happen. I thought about millis()
rollover, but that would take more than a few days, wouldn't it?
Any clue? Code is below:
const int timeLedOn = 20;
const int timeLedOff = 7 * timeLedOn;
const int ledPin = 8;
int buttonLevel = LOW;
int previousButtonLevel = LOW;
int ledState = LOW;
bool blinkingTurnedOn = false;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
volatile unsigned long lastMicros;
long debouncingTime = 1000 * 200;
void setup() {
// use interrupt 0 (pin 2) for
attachInterrupt(0, debounceInterrupt, RISING);
pinMode(ledPin, OUTPUT);
// disable onboard led
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// turn led off
digitalWrite(ledPin, LOW);
}
void loop() {
currentMillis = millis();
if (blinkingTurnedOn) {
performBlinking();
}
else {
digitalWrite(ledPin, LOW);
}
}
void debounceInterrupt() {
if ((long)(micros() - lastMicros) > debouncingTime) {
toggleBlinking();
}
lastMicros = micros();
}
void toggleBlinking() {
blinkingTurnedOn = !blinkingTurnedOn;
}
void performBlinking() {
int timeDelta = currentMillis - previousMillis;
// check if time "off" elapsed
bool elapsedOff = ledState == LOW && timeDelta > timeLedOff;
// check if time "on" elapsed
bool elapsedOn = ledState == HIGH && timeDelta > timeLedOn;
// blinking itself
if (elapsedOff || elapsedOn) {
toggleLedState();
}
}
void toggleLedState() {
ledState = 1 - ledState;
digitalWrite(ledPin, ledState);
resetMillis();
}
void resetMillis(){
previousMillis = currentMillis;
}
I suspect the reason for the delay may be that the blinkingTurnedOn
flag is not marked as volatile
(in the same way as the lastMicros
variable). Since it is changed (indirectly) from within an interrupt its new value may not be immediately visible to the code called from loop()
without the volatile
modifier to tell it to always read the value from memory (rather than assuming the current value in a register is up-to-date). Arduino reference.
Changing the declaration to:
volatile bool blinkingTurnedOn = false;
should fix the problem.