Momentary Button Sometimes Doesn't Respond

I have a code that cycles and RGB LED through the rainbow and then I have a Momentary Button that pauses the cycle when pressed. Sometimes the button press doesn't register and I have a feeling it's not a programming issue but more to do with how the button itself works. Anyone know what could cause this or how I can fix this? If more information is needed let me know.

How is the switch wired? The best way is to wire one side to ground and the other side to an input set to pinMode INPUT_PULLP. The switch will read HIGH when not pressed and LOW when pressed.

If that does not help, post the code and a schematic of the wiring.

I have it wired as you mentioned, here’s the code.

int redPin = 11;
int greenPin = 10;
int bluePin = 9;
int buttonPin = 2;

int r = 0;
int g = 0;
int b = 0;
int temp = 1;

void setup() {
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(buttonPin), Pause, FALLING);

}

void loop() {

  if (temp == 1) {
    setColor(0, 255, 255);  // aqua
    setColor(0, 80, 80);    // dark blue/green
    setColor(0, 255, 0);    // green
    setColor(213, 255, 0);  // lime green
    setColor(255, 255, 0);  // yellow
    setColor(204, 102, 0);  // orange
    setColor(255, 60, 0);   // red/orange
    setColor(255, 0, 0);    // red
    setColor(0, 0, 255);    // blue
  }
  else {
    // do nothing
  }

}

void setColor(int red, int green, int blue) {
  while ( r != red || g != green || b != blue ) {
    if ( r < red ) r += 1;
    if ( r > red ) r -= 1;

    if ( g < green ) g += 1;
    if ( g > green ) g -= 1;

    if ( b < blue ) b += 1;
    if ( b > blue ) b -= 1;

    _setColor();
    delay(8);             // fade delay
    if (temp == 0) {
      return;
    }
  }
}

void _setColor() {
  analogWrite(redPin, r);
  analogWrite(greenPin, g);
  analogWrite(bluePin, b);
}

void Pause()
{
  if (temp == 1) {
    temp = 0;
  }
  else {
    temp = 1;
  }
  delay(200);
}

Delay() does not work in an ISR. Interrupts are disabled in an ISR. Delay() uses interrupts.

Oh, any suggestions? I understand what you pointed out, unfortunately I'm not skilled in programming to come up with a new solution but if you can point me the right way I can look into it.

You are obviously using delay() for a switch debounce...

Although delay() doesn't work in an ISR, millis() does return a valid result, however it will not advance while the ISR is running - it's "frozen". Although that sounds serious, in fact you can use it for timing just like you would in loop() by storing the last time the interrupt was called, and comparing that to the current value.

The general logic goes like this:

First thing you do in the ISR, check to see if the difference between millis() and the stored time is less than the debounce interval. If it is, do nothing except return from the ISR. If it isn't, toggle your 'temp' variable and store millis().

BTW, persistent (non-throwaway) variables in an ISR have to be declared with the 'static' keyword.

void Pause()
{
  static unsigned long lastMillis;

  if (millis() - lastMillis >= 200) {
    if (temp == 1) {
      temp = 0;
    }
    else {
      temp = 1;
    }
  lastMillis = millis();
  }
}

You should try reducing the delay interval once you have it working. 200ms is an insanely long time for a switch.

Thank you! I'll give that a try tomorrow :).

In short, unless you understand interrupts, do not attempt to use interrupts. :grinning:

"Stock" explanation:


That is not what interrupts are for!

As a beginner, it is incredibly unlikely that interrupts will be useful to you.

A common "newbie" misunderstanding is that an interrupt is a mechanism for altering the flow of a program - to execute an alternate function. Nothing could be further from the truth! :astonished:

An interrupt is a mechanism for performing an action which can be executed in "no time at all" with an urgency that it must be performed immediately or else data - information - will be lost or some harm will occur. It then returns to the main task without disturbing that task in any way though the main task may well check at the appropriate point for a "flag" set by the interrupt.

Now these criteria are in a microprocessor time scale - microseconds. This must not be confused with a human time scale of tens or hundreds of milliseconds or indeed, a couple of seconds. A switch operation is in this latter category and even a mechanical operation perhaps several milliseconds; the period of a 6000 RPM shaft rotation is ten milliseconds.

Unless it is a very complex procedure, you would expect the loop() to cycle many times per millisecond. If it does not, there is most likely an error in code planning; while the delay() function is provided for testing purposes, its action goes strictly against effective programming methods. The loop() will be successively testing a number of contingencies as to whether each requires action, only one of which may be whether a particular timing criteria has expired. Unless an action must be executed in the order of microseconds, it will be handled in the loop().

So what sort of actions do require such immediate attention? Well, generally those which result from the computer hardware itself, such as high speed transfer of data in UARTs(, USARTs) or disk controllers.

An alternate use of interrupts, for context switching in RTOSs, is rarely relevant to this category of microprocessors as it is more efficient to write cooperative code as described above.

Oh wow, thanks for the great explanation.

The point is that you need to divide up the "rainbow" animation into parts - in your case between each "setColor" instruction - and put the "pause" code which checks on the button and performs the delay, in between.

Since you call "setColor" each time, you could simply put the pause in that subroutine. There is never any doubt whether the button will respond as it is directly checked at each step.

And of course, there are a lot of enhancements to be made to that program, you could put the colour pattern in an array and use loop() to read one pattern after another from the array, check the switch, and display it, on the next pass through loop() you read the next pattern from the array etc.

Maybe later tonight ... :grinning: