Stop a function with a button

Hi!

I am trying to make a led strip with different modes.
Currently I have managed to make a interrupt, with a function that increments a variabel, when i push a button, so the switch changes. The problem I have, is that I can't manage to make the function stop right at the moment when I press the button. I do not know how to do this, other then to put a loop that always checks the button if this is pressed, inside the redTurnOn() function, and all the other colorfunctions. I have been taught that this is bad programming, so i want to make a interrupt to do this.

Don't bother with the commented functions, I am also trying to make one function that I can use for all the colors, but I have not worked on it that much, so currently the colorfunctions is what I use. I also don't use the checkIfButtonPress() function. This was in the early state of the program.

This is my first project, so the coding may not be perfect, hehe.

Please help:)

//define pins for the red
#define RED_LED 6
#define BLUE_LED 9
#define GREEN_LED 5
#define BUTTON 2

int brightness = 255;
int gBright = 0;
int rBright = 0;
int bBright = 0;
int fadeSpeed = 10;

int transition = 100;

volatile int switchState = 0;
int modeNumb = 1;

void setup() {
  pinMode(BUTTON,INPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(BUTTON), incrementSwitch, RISING);
}

/*void turnOnFadeLed(int color, int led){
  for (int i=0;i<255; i++){
    color ++;
    analogWrite(led, color);
    delay(fadeSpeed);
    //Serial.print("TurnOnLedNumb");
    //Serial.println(modeNumb);
  }
}

void turnOffFadeLed(int color,int led){
  for (int i=0;i<255; i++){
    color--;
    analogWrite(led, color);
    delay(fadeSpeed);
    //Serial.print("turnOffLedNumb");
    //Serial.println(modeNumb);
  }
}

void TurnOnFade(){
  turnOnFadeLed(rBright,RED_LED);
  if (gBright == 255){
    turnOffFadeLed(gBright,GREEN_LED);
  }
  turnOnFadeLed(bBright,BLUE_LED);
  turnOffFadeLed(rBright,RED_LED);
  turnOnFadeLed(gBright,GREEN_LED); 
  turnOffFadeLed(bBright,BLUE_LED);
}*/


void redTurnOn(){ 
  for (int i=0;i<brightness; i++){
    rBright ++;
    analogWrite(RED_LED, rBright);
    delay(fadeSpeed);
  }
}

void greenTurnOn(){
  for (int i=0;i<brightness; i++){
    gBright ++;
    analogWrite(GREEN_LED, gBright);
    delay(fadeSpeed);
  } 
}

void blueTurnOn(){
  for (int i=0;i<brightness; i++){
    bBright ++;
    analogWrite(BLUE_LED, bBright);
    delay(fadeSpeed);
  }
}

void redTurnOff(){
  for (int i=0;i<brightness; i++){
    rBright --;
    analogWrite(RED_LED, rBright);
    delay(fadeSpeed);
  }
}

void blueTurnOff(){
  for (int i=0;i<brightness; i++){
    bBright --;
    analogWrite(BLUE_LED, bBright);
    delay(fadeSpeed);
  }
}

void greenTurnOff(){
  for (int i=0;i<brightness; i++){
    gBright --;
    analogWrite(GREEN_LED, gBright);
    delay(fadeSpeed);
  } 
}

void redOn(){
  rBright = brightness;
  analogWrite(RED_LED, rBright);
  delay(transition);
}

void greenOn(){
  gBright = brightness;
  analogWrite(GREEN_LED, gBright);
  delay(transition);
}

void blueOn(){
  bBright = brightness;
  analogWrite(BLUE_LED, bBright);
  delay(transition);
}

void redOff(){
  rBright = 0;
  analogWrite(RED_LED, rBright);
  delay(transition);
}

void greenOff(){
  gBright = 0;
  analogWrite(GREEN_LED, gBright);
  delay(transition);
}

void blueOff(){
  bBright = 0;
  analogWrite(BLUE_LED, bBright);
  delay(transition);
}

void colorChange(){
  blueOn();
  if (rBright == 255){
    redOff();
  }
  greenOn();
  blueOff();
  redOn();
  greenOff();
  
}

void TurnOnFade(){
    redTurnOn();
    if (gBright == brightness){
      greenTurnOff();
    }
    blueTurnOn();
    redTurnOff();
    greenTurnOn(); 
    blueTurnOff();
}

void redStrobe(){
  redOn();
  redOff();
}

void resetBrightnessValue(){
  rBright = 0;
  analogWrite(RED_LED, rBright);
  gBright = 0;
  analogWrite(GREEN_LED, gBright);
  bBright = 0;
  analogWrite(BLUE_LED, bBright);
}

bool checkIfButtonPress(){
  switchState = digitalRead(BUTTON);
  if (switchState == 1){
    delay(300);
    return true;
  }
  else{
    switchState = 0;
    return false;
  }
}

void incrementSwitch(){
  if (modeNumb < 4){
    modeNumb ++;
    delay(300);
    Serial.print("incrementSwitch modeNumb: ");
    Serial.println(modeNumb);
  }
  else {
    modeNumb = 1;
    delay(300);
    Serial.print("incrementSwitch modeNumb change to 1: ");
    Serial.println(modeNumb);
  }
}

void changeOfModes(){
  Serial.print("modenumb: ");
  Serial.println(modeNumb);
  switch(modeNumb){
  case 1:{
    resetBrightnessValue();
    while (modeNumb == 1){
      TurnOnFade();
      Serial.print("modenumbCase1: ");
      Serial.println(modeNumb);
    }
    break;
  }
  case 2:{
    resetBrightnessValue();
    while (modeNumb == 2){
      redTurnOn();
      redOff();
      //turnOnFadeLed(rBright,RED_LED);
      Serial.print("modenumbCase2: ");
      Serial.println(modeNumb);
    }
    break;
  }
  case 3:{
    resetBrightnessValue();
    while (modeNumb == 3){
      colorChange();
      Serial.print("modeNumbCase3: ");
      Serial.println(modeNumb);
    }
    break;
  }
  case 4:{
    resetBrightnessValue();
    while (modeNumb == 4){
      redStrobe();
      Serial.print("modeNumbCase4: ");
      Serial.println(modeNumb);
    }
    break;
  }
  }
}

void loop() {
  changeOfModes();
}

never do serial prints in the interrupt routine

1 Like

Your interrupt must be efficient, I recommend declaring a global bool and making it volatile. The interrupt just toggles the bool to false, you will need to set it true before calling any function to interrupt.
Then, use a do-while-true construct in your function which will be exited when the global var changes to false.

https://www.arduino.cc/reference/en/language/structure/control-structure/dowhile/

don't do this in your ISR

2 Likes

Hello
I´ve made a low level flight about your sketch. The sketch seems to be groth organicaly.
My notes:
To operate a button an ISR is not needed. A simple button manager will do this job more convient. A FSM and time handler are able to handle the different LED illuminations events.
Take some time and study the OOP elements like enum, array, struct etc to get a smarter solution for this task.
Have a nice day and enjoy coding in C++.

1 Like

@peraka, your topic has been moved to a more suitable location on the forum. This has nothing to do with Interfacing w/ Software on the Computer as far as I can see.

  1. You must not use delay() or Serial.print() in an interrupt. These functions rely on interrupts and interrupts are disabled during the ISR.
  2. Any variable referenced in an interrupt must be declared with the volatile keyword. Any multibyte variable shared between the interrupt and normal processing must only be accessed while interrupts are disabled. This is specific to this hardware architecture.
  3. Interrupts are NOT necessary for button processing and usually cause more problems than they solve and make the code harder to debug.

I'm not sure where beginners get the idea that interrupts are the best solution. You will rarely need to use interrupts unless you are performing very low-level and time sensitive operations, in which case you are not a beginner.

1 Like

While it’s true, they are preferable for button processing, unless of course your board has no other functions but polling a button state.

1 Like

True but actually print does take care of this and will do the right thing if interrupts are disabled, so you won't end up stuck in the ISR

(still a bad idea though, kinda OK-ish for short debug and seeing the ISR is called. Turning on the builtin LED is probably a better idea)

Why would interrupts be "preferable"? I have written dozens of Arduino applications without using interrupts for button processing.

1 Like

-1. That is to say I disagree, preferred by whom?

a7

I gave the reason, you need to poll them often or you will have unresponsive buttons, but if your code is doing something else when you press button it will not register.

common sense

It's a problem only if "something else" takes more than, say, 10 ms at a time. A user can't detect debouncing or button delays up to about 20 ms. It's not that difficult once you understand the BWOD techniques.

If your code is so busy it can't process a button press then it may need to be refactored into smaller tasks. Again, I have written dozens of applications with the Arduino and haven't missed a button press yet and I don't use interrupts for buttons.

Keep telling yourself that. The truth is there are plenty of poor, slow, sluggish, unoptimised arduino projects out there when in fact the boards are brilliant and are capable of top performance when used correctly.

Not even going to comment

Why? Because you can't figure this out?

A seasoned programmer can use interrupts for buttons quite easily, no doubt. For a beginner I think it causes too much confusion and is most always unnecessary. Indeed it is rarely necessary for well-written code as well.

1 Like

Of course there are edge cases where you want to react super fast, like emergency cut-off or motor rotary encoders etc... There is a place for interrupts but for most codes we see here, a simple polling button library is fine and it teaches users to get faster loops...

The average typing speed is around 40 words per minute, say a word is 6 characters long, that's 240 key pressed in 60s ➜ ~250ms per key

So a typical human button press is easily above 0.1s long... Your code would have to be really busy "doing something else" to miss this. And if it's sooo busy, you might register the keypress in your ISR but it won't be after your busy loop that it will be taken into account so the user will likely feel the lag..

The truth is there are plenty of poor, slow, sluggish, unoptimised arduino projects out there

Such a code would be one of those indeed. but you are right there are tons of unoptimised arduino projects out there for sure.

I’m a glass half full kinda person so I’d say there are edge cases when you don’t care how fast your code reacts to button pushes. Granted some won’t even notice the lag even if their life depended on it. So I will stick to my guns and say learn interrupts if you care about UX.